# -*- coding: utf-8 -*-
import simplejson
from openerp.addons.dftg_ext.controllers.pyh import table, tr, td, th
from openerp.modules.registry import RegistryManager
from openerp.addons.dftg_ext import dftg_decorate
from openerp.osv import fields
from openerp.osv import osv
import openerp.addons.decimal_precision as dp
from datetime import date, timedelta, datetime
from openerp import SUPERUSER_ID
import redis
from operator import attrgetter
from openerp.tools.translate import _
from openerp import SUPERUSER_ID


class purchase_order_line(osv.osv):
    # 采购单字段扩展

    _inherit = 'purchase.order.line'
    _description = u'入库助手明细'

    # 计算预计到货数
    def _compute_incoming(self, cr, uid, ids, name, arg, context=None):
        res = {}
        for order_line in self.browse(cr, uid, ids, context=context):
            if order_line.product_qty > 0:
                incoming_qty = order_line.product_qty - order_line.incomed_qty
            else:
                incoming_qty = order_line.product_qty + order_line.incomed_qty
            res.update({order_line.id: incoming_qty})
        return res

    # 因需要重载字段price_subtotal, 复制odoo自带的函数计算函数
    def _amount_line(self, cr, uid, ids, prop, arg, context=None):
        return super(purchase_order_line, self)._amount_line(cr, uid, ids, prop, arg, context=context)

    # “申请修改”后，修改数量不能小于已入库数
    def onchange_product_qty(self, cr, uid, ids, product_qty,parent_state, context=None):
        for order_line in self.browse(cr, uid, ids, context=context):
            if order_line.delivered_qty and product_qty < order_line.delivered_qty:
                return {'value': {'product_qty': order_line.product_qty},
                        'warning': {'title': u'警告', 'message': u'修改数量不能小于已建发货单的数量'}}
        if parent_state != 'draft':
            return {'value': {'product_qty': '', 'product_id': False},
                    'warning': {'title': u'警告', 'message': u'采购订单在确认状态下不能新建订单行，请申请修改后再添加'}}

    def delete_sale_order_line_change(self, cr, uid, ids, context=None):
        """"本方法用来删除冗余的订单行变更历史  应用场景 setp1 新建销售订单 确认 ,setp2,刷新采购寻求
        setp3 采购助手找到新建的销售订单对应的产品 生成新建采购单 step4. 修改销售订单 的产品(删除原有的产品新增 一个其他的产品)确认订单
        step5 删除刚刚生成的采购单 (这时这个销售订单变更历史存在 产品相同 型号相同 数量相加为零的产品 (都是未处理的) 这多条条记录的 存在会造成不必要的操作的麻烦
            所以进行删除"""
        sale_order_list, prodcut_id_list, socl_ids = [], [], []
        for purchase_order_line in self.browse(cr, uid, ids, context=context):
            if purchase_order_line.order_id.so_ids and eval(purchase_order_line.order_id.so_ids):
                sale_order_list.extend(eval(purchase_order_line.order_id.so_ids))
            prodcut_id_list.append(purchase_order_line.product_id.id)
        # 取得销售订单变更历史 中  po_line_id  po_id 为空  so_id  product_id 为传进来的采购单行的数据
        #  取出 销售订单变更历史存在 产品相同 型号相同 数量相加为零的产品 (都是未处理的) 结果为 [{{22,99},0},{{4555,666},0}]
        cr.execute('''select *  from (select array_agg(id) as solc,
                   sum(change_quantity) as change_quantitys
                   from sale_order_line_change where so_id in %s and product_id in %s and po_line_id is null and po_id is null
                    group by product_id,so_id,po_line_id,prod_spec) as a
                   where a.change_quantitys=0
            ''' % (str(sale_order_list).replace('[', '(').replace(']', ')'), str(prodcut_id_list).replace('[', '(').replace(']', ')')))
        [socl_ids.extend(list(row[0])) for row in cr.fetchall()]
        self.pool.get('sale.order.line.change').unlink(cr, uid, socl_ids, context=context)

    def create(self, cr, uid, vals, context=None):
        if vals.get('prod_spec'):
            vals.update({'prod_spec': vals.get('prod_spec') and (vals.get('prod_spec')).strip()})
        if not vals.get('expected_delivery_time', False):
            order_row = self.pool.get('purchase.order').browse(cr, uid, vals.get('order_id'), context=context)
            vals.update({'expected_delivery_time': order_row.planned_delivery_date})
        return super(purchase_order_line, self).create(cr, uid, vals, context=context)

    def write(self, cr, uid, ids, vals, context=None):
        """
        成本价变更就直接修改对应的move 对应的quant
        """
        move_obj = self.pool.get('stock.move')
        if vals.get('prod_spec'):
            vals.update({'prod_spec': (vals.get('prod_spec')) and (vals.get('prod_spec')).strip()})
        delivered_qty = vals.get('delivered_qty')
        if None != delivered_qty and delivered_qty < 0:
            raise osv.except_osv(u'错误', u'已建入库单数量为负数, 请联系系统管理员')
        return_vals = super(purchase_order_line, self).write(cr, uid, ids, vals, context=context)
        if vals.get('price_unit', 'default_vals') != 'default_vals':
            ids = [ids] if type(ids) == type(1) else ids
            for purchase_line_id in ids:
                move_ids = move_obj.search(cr, uid, [('purchase_line_id', '=', purchase_line_id)], context=context)
                if move_ids:
                    move_obj.set_quant_cost(cr, uid, move_ids, context=context)
        return return_vals

    def unlink(self, cr, uid, ids, context=None):
        """当删除采购单行的时候 需要把销售订单变更历史中的 对应这条采购单 的变更历史 对应的采购单 和采购单行 字段清空"""
        purchase_order_line_ids = self.pool.get('sale.order.line.change').search(cr, uid, [('po_line_id', 'in', ids)], context=context)
        for po_line in self.browse(cr, uid, ids, context=context):
            if po_line.delivered_qty > 0:
                raise osv.except_osv(u'错误', u'%s 已建了入库单, 请先取消入库单' % po_line.name)
        if purchase_order_line_ids:
            self.pool.get('sale.order.line.change').write(cr, uid, purchase_order_line_ids, {'po_line_id': False, 'po_id': False}, context=context)
            self.delete_sale_order_line_change(cr, uid, ids, context=context)
        return super(purchase_order_line, self).unlink(cr, uid, ids, context=context)

    _columns = {
        'incomed_qty': fields.float(u'已入库数', digits_compute=dp.get_precision('Product Unit of Measure'),copy=False),
        'incoming_qty': fields.function(_compute_incoming, string=u'未入库数', type='float', digits_compute=dp.get_precision('Product Unit of Measure'), store={
            'purchase.order.line': (lambda self, cr, uid, ids, c={}: ids, ['incomed_qty', 'product_qty'], 11)
        }, copy=False),
        'delivered_qty': fields.float(u'已建入库单数量', digits_compute=dp.get_precision('Product Unit of Measure'),copy=False),
        'prod_spec': fields.text(u"型号备注"),
        'plan_arrive_date': fields.related('order_id', 'plan_arrive_date', type='date', string=u'计划到达日期', readonly="1", copy=False),
        'prod_model': fields.related('product_id', 'prod_model', type='char', string=u'产品型号', readonly="1"),
        'price_subtotal': fields.function(_amount_line, string='Subtotal', digits_compute=dp.get_precision('Account'), type='float', store={
            'purchase.order.line': (lambda self, cr, uid, ids, c={}: ids, ['price_unit', "product_qty"], 9),
        }),
        'purchase_spec': fields.char(u'采购备注', copy=False),
        'origin_prod_spec': fields.related('product_id', 'prod_spec', type='char', string='产品规格', readonly=True,
                                           store=True),
        'po_note': fields.related('order_id','notes',type='text',string='采购单备注'),
        'expected_delivery_time': fields.date(u'计划到货日期')
    }
    _defaults = {
        'incomed_qty': 0,
        'delivered_qty': 0,

    }


class purchase_order(osv.osv):
    # 增加入库助手界面
    _inherit = 'purchase.order'
    _description = u'入库助手'

    def init(self, cr):
        cr.execute("""
            INSERT INTO "purchase_order"("id","origin","create_date","company_id","currency_id","partner_ref","date_order",
                "partner_id","dest_address_id","create_uid","fiscal_position","amount_untaxed","picking_type_id","location_id",
                "message_last_post","journal_id","amount_tax","state","bid_validity","pricelist_id","incoterm_id","bid_date",
                "payment_term_id","write_date","write_uid","date_approve","amount_total","name","notes","invoice_method","shipped",
                "validator","minimum_planned_date","related_location_id","shipping_method","income_finished_time","stock_memo",
                "phone_number","is_print_price","invoice_type","fax_number","so_ids","mobile_number","voucher_id","advance_payment",
                "print_state","purchaser_check_time","so_code_list","is_purchase_checked","pick_state","contacts_id",
                "is_incoming_picking_done","is_picking_button_assigned","is_partner_alterable","is_direct_delivery",
                "planned_delivery_date","is_exchange","is_return","is_flagged_one","develiey_address","is_self_pickup",
                "supplier_address","is_card","is_manual_create_stock")
              SELECT
                  -1,NULL,E'2016-03-07 08:30:39.188008',1,8,NULL,E'2016-03-07 08:30:39',1,NULL,1,NULL,0,1,12,NULL,3,0,E'done',
                  NULL,2,NULL,NULL,NULL,E'2016-06-21 01:11:30.895957',1,E'2016-03-07',0,E'P库存保留',NULL,E'picking',TRUE,1,
                  E'2016-03-07',12,NULL,NULL,NULL,NULL,FALSE,NULL,NULL,NULL,NULL,NULL,0,TRUE,NULL,NULL,FALSE,TRUE,NULL,
                  TRUE,TRUE,FALSE,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL
                WHERE
                NOT EXISTS (
                SELECT id FROM purchase_order WHERE id = -1 )
        """)

    def delete_purchase_card(self, cr, uid, po_id, context=None):
        card_line_obj = self.pool.get('product.card.line')
        card_pass_obj = self.pool.get('product.card.password')
        card_line_ids = card_line_obj.search(cr, uid, [('po_id', '=', po_id)], context=context)
        card_pass_ids = card_pass_obj.search(cr, uid, [('line_id', 'in', card_line_ids)], context=context)
        card_pass_obj.unlink(cr, uid, card_pass_ids, context=context)
        return card_line_obj.unlink(cr, uid, card_line_ids, context=context)

    # 重载unlink方法，当删除采购单时，把对应的预付款凭证也删除，如果此付款凭证已记账，则提示
    def unlink(self, cr, uid, ids, context=None):
        account_voucher_obj = self.pool.get("account.voucher")
        order_line_ids = []
        for purchase in self.browse(cr, uid, ids, context=context):
            if purchase.is_card:
                self.delete_purchase_card(cr, uid, purchase.id, context=context)
            order_line_ids.extend([order_line.id for order_line in purchase.order_line])
            # 如果存在预付款凭证
            if purchase.voucher_id:
                for voucher in account_voucher_obj.browse(cr, uid, purchase.voucher_id.id, context=context):
                    # 预付款凭证已记账
                    if voucher.state in ('posted'):
                        raise osv.except_osv(
                            u'错误', u'相关单据预付款凭证已经记账，无法删除，请先将对应预付款凭证取消记账后再删除采购单')
                    # 预付款凭证未记账
                    if voucher.state in ('cancel', 'draft', 'proforma'):
                        account_voucher_obj.unlink(
                            cr, uid, [voucher.id], context=context)
            if purchase.picking_ids:
                raise osv.except_osv(
                    u'错误', u"此采购单已存在入库单不能删除！")
            if purchase.invoice_ids:
                raise osv.except_osv(
                    u'错误', u"此采购单已存在发票不能删除！")
        purchase_order_line_ids = self.pool.get('sale.order.line.change').search(cr, uid, [('po_id', 'in', ids)], context=context)
        if purchase_order_line_ids:
            self.pool.get('sale.order.line.change').write(cr, uid, purchase_order_line_ids, {'po_line_id': False, 'po_id': False}, context=context)
            self.pool.get('purchase.order.line').delete_sale_order_line_change(cr, uid, order_line_ids, context=context)
        res = super(purchase_order, self).unlink(cr, uid, ids, context=context)
        return res

    def create(self, cr, uid, vals, context=None):
        # 同一采购单只能采购相同产品
        purchase_order_id = super(purchase_order, self).create(
            cr, uid, vals, context=context)
        purchase_order_obj = self.browse(
            cr, uid, purchase_order_id, context=context)
        product_list = []
        # IFO4V 去除采购单保存后的送货地址后面的采购单编号逻辑
        # if purchase_order_obj.develiey_address and ' ' not in purchase_order_obj.develiey_address and (
        #     "-P%s" % purchase_order_obj.name[-3:]) \
        #         not in purchase_order_obj.develiey_address:
        #     raise osv.except_osv(u'错误', u'请在送货地址字段中，用空格分割地址和联系人手机号!')
        # if purchase_order_obj.develiey_address and \
        #     ("-P%s" % purchase_order_obj.name[-3:]) not in purchase_order_obj.develiey_address and \
        #         ' ' in purchase_order_obj.develiey_address:
        #     first_space = purchase_order_obj.develiey_address.index(' ')
        #     self.write(cr, uid, purchase_order_obj.id, {
        #         "develiey_address": "%s-P%s%s" % (purchase_order_obj.develiey_address[0:first_space],
        #                                           purchase_order_obj.name[-3:],
        #                                           purchase_order_obj.develiey_address[first_space:])},
        #                context=context)
        is_add = False
        is_minus = False
        for order_line in purchase_order_obj.order_line:
            product_list.append((order_line.product_id.id,))
            if order_line.product_qty > 0 and not is_add:
                is_add = True
            if order_line.product_qty < 0 and not is_minus:
                is_minus = True
        # if is_minus:  # 采购退货部分放宽限制，可以直接创建采购退货订单，不必要走退货向导
        #     raise osv.except_osv(u'错误', u"不能手动创建采购退货单（如需退货请在更多里面用向导退货）!")
        if not is_add and is_minus:
            model_data_obj = self.pool.get('ir.model.data')
            picking_type_id = model_data_obj.xmlid_to_res_id(cr, SUPERUSER_ID, 'dftg_ext.purchase_order_return_picking_type')
            self.write(cr, uid, purchase_order_id, {
                "is_return": True, 'picking_type_id': picking_type_id}, context=context)
        elif is_add and is_minus:
            self.write(cr, uid, purchase_order_id, {
                       "is_exchange": True}, context=context)
        
        if len(product_list) > len(list(set(product_list))):
            raise osv.except_osv(
                u'错误', u'一个产品在一个采购单中只能存在一个采购单行')
        if vals.get('is_direct_delivery', 'default_vals') != 'default_vals':
            singe = vals.get('is_direct_delivery')
            so_ids = []
            if vals.get('so_ids'):
                so_ids = eval(vals.get('so_ids'))
            self.is_direct_delivery_operation(cr, uid, so_ids,
                                              list(set([product[0] for product in product_list])), purchase_order_id, singe, context=context)
        return purchase_order_id
    
    def get_direct_location(self, cr):
        model_data_obj = self.pool.get('ir.model.data')
        direct_location_id = model_data_obj.xmlid_to_res_id(cr, SUPERUSER_ID, 'dftg_ext.stock_location_direct')
        return direct_location_id
    
    def is_direct_delivery_operation(self, cr, uid, ids, product_list, purchase_order_id, singe, context=None):
        """如果采购单生成 的直运状态改变 则 修改采购单 对应的销售订单 及stock_move 表上的 是否是直运订单的 字段"""
        move_obj = self.pool.get("stock.move")
        picking_obj = self.pool.get('stock.picking')
        for sale_order in self.pool.get("sale.order").browse(cr, uid, ids, context=context):
            for order_line in sale_order.order_line:
                if order_line.product_id.id in product_list:
                    order_line.write({"is_direct_delivery": singe})
        purchase_order_row = self.browse(cr, uid, purchase_order_id, context=context)
        move_ids = move_obj.search(cr, uid, [('picking_id', 'in', [picking_ids.id for picking_ids in
                                                                   purchase_order_row.picking_ids]),
                                             ('state', '!=', 'cancel')], context=context)
        all_state = [move.state for move in move_obj.browse(cr, uid, move_ids, context=context)]
        if 'done' in all_state:
            raise osv.except_osv(u'错误', u'没有可供操作的入库单！（入库单已取消（请重新生成）或者已经完成（不可修改是否直送））')
        if not singe:
            picking_ids = picking_obj.search(cr, uid, [('so_id', 'in', ids),
                                                       ('state', 'not in',
                                                        ('cancel', 'done', 'assigned', 'partially_available'))],
                                             context=context)
            if picking_ids:
                picking_obj.unlink(cr, uid, picking_ids, context=context)
        if move_ids:
            location_id = self.get_direct_location(cr) if singe else purchase_order_row.location_id.id
            self.pool.get("stock.move").write(cr, uid, move_ids, {"is_direct_delivery": singe,
                                                                  'location_dest_id': location_id}, context=context)
            
    def write(self, cr, uid, ids, vals, context=None):
        # 同一采购单只能采购相同产品
        super(purchase_order, self).write(
            cr, uid, ids, vals, context=context)
        for purchase_obj in self.browse(cr, uid, ids, context=context):
            # IFO4V 去除采购单保存后的送货地址后面的采购单编号逻辑
            # if purchase_obj.develiey_address and ' ' not in purchase_obj.develiey_address and ("-P%s"%purchase_obj.name[-3:])\
            #         not in purchase_obj.develiey_address:
            #     raise osv.except_osv(u'错误', u'请在送货地址字段中，用空格分割地址和联系人手机号!')
            # if purchase_obj.develiey_address and \
            #     ("-P%s"%purchase_obj.name[-3:]) not in purchase_obj.develiey_address and \
            #     ' ' in purchase_obj.develiey_address:
            #     first_space = purchase_obj.develiey_address.index(' ')
            #     self.write(cr, uid, purchase_obj.id,{
            #         "develiey_address": "%s-P%s%s"%(purchase_obj.develiey_address[0:first_space],
            #                                         purchase_obj.name[-3:],
            #                                         purchase_obj.develiey_address[first_space:])},
            #                context=context)
            if vals.get("planned_delivery_date", 'default_vals') != 'default_vals':
                purchase_obj.order_line.write({'expected_delivery_time': vals.get("planned_delivery_date")})
            if vals.get("partner_id", 'default_vals') != 'default_vals' and purchase_obj.state!='draft':
                raise osv.except_osv(
                    u'错误', u'在确认状态不允许修改供应商')
            if purchase_obj.shipment_count!=0 and vals.get("partner_id", 'default_vals') != 'default_vals':
                raise osv.except_osv(
                    u'错误', u'已有入库单不允许修改供应商，请取消入库单再进行修改！')
            if purchase_obj.invoice_ids and vals.get("partner_id", 'default_vals') != 'default_vals':
                raise osv.except_osv(
                    u'错误', u'此采购单已有相关发票，不能修改供应商，取消发票再进行修改！')
            if vals.get("is_direct_delivery", 'default_vals') != 'default_vals':
                so_ids = eval(purchase_obj.so_ids)
                if not so_ids and vals.get("is_direct_delivery"):
                    raise osv.except_osv(u'错误', u'此采购单没有关联销售订单, 无法选择直送')
                if so_ids and so_ids[0]:
                    sale_order_objs = self.pool.get('sale.order').browse(cr, uid, so_ids[0], context=context)
                    direct_delivery = 'direct_delivery' if vals.get("is_direct_delivery") else False
                    sale_order_objs[0].write({'shipping_type_sale': direct_delivery})
            product_list = []
            if vals.get("order_line"):
                is_return = purchase_obj.is_return
                is_exchange = purchase_obj.is_exchange
                is_add = False
                is_minus = False
                for order_line in purchase_obj.order_line:
                    # 同一订单中不允许存在 产品种类和数量的正负 一样的订单行
                    product_list.append((order_line.product_id.id,))
                    if is_return and order_line.product_qty > 0:
                        raise osv.except_osv(u'错误', u"退货单中产品数量不能为正!")
                    if order_line.product_qty > 0 and not is_add:
                        is_add = True
                    if order_line.product_qty < 0 and not is_minus:
                        is_minus = True
                # if (not (is_minus and is_add)) and is_exchange:
                #     raise osv.except_osv(u'错误', u"订单修改不正确，换货单中产品数量必须有正有负!")
                # if (not is_minus or is_add) and is_return:
                #     raise osv.except_osv(u'错误', u"订单修改不正确，退货单中必须只有负数的产品!")
                # if not (is_return or is_exchange) and is_minus:
                #     raise osv.except_osv(u'错误', u"订单修改不正确，正常采购订单中不能有负数的产品!")
                if is_minus and not is_return and not is_exchange:
                    raise osv.except_osv(u'错误', u"请在原采购单上点击更多里的退货向导进行退货操作!")
                if len(product_list) > len(list(set(product_list))):
                    raise osv.except_osv(
                        u'错误', u'一个产品在一个采购单中只能存在一个采购单行')
            if vals.get('is_direct_delivery', "no_change") != "no_change":  # 修改对应单据的直运状态
                singe = vals.get('is_direct_delivery')
                self.is_direct_delivery_operation(cr, uid, eval(purchase_obj.so_ids),
                                                  list(set([product[0] for product in product_list])), purchase_obj.id, singe, context=context)
        return ids

    # 继承odoo系统的采购单"确认按钮"的逻辑，并且将采购价格作为成本价填充产品的成本价字段standard_price

    def wkf_confirm_order(self, cr, uid, ids, context=None):
        wizard_obj = self.pool.get('generate.purchase.order.wizard')
        for po in self.browse(cr, uid, ids, context=context):
            if po.partner_id.name == '未指定供应商':
                raise osv.except_osv(u"错误", u"未指定供应商，不能确认采购单")
            if po.partner_id.is_auto_write_price:
                for pol in po.order_line:   # #采购单产品价格回写产品的功能重新开启
                    if pol.price_unit != 0:
                        pol.product_id.write({'standard_price': pol.price_unit})
            if po.state == 'draft' and not po.is_return: #退货订单不用在这个地方创建入库单
                # 不知为何这个方法会执行两次（应该是哪个地方书写不对引起的work_folw方法触发两次）
                self.pool.get('batch.generation.stock.picking'
                              ).generation_create_stock_picking(cr, uid, ids, context=context)
                if po.is_direct_delivery:
                    wizard_obj.create_sale_order_out_picking(cr, uid, po.id, context=context)
        return super(purchase_order, self).wkf_confirm_order(cr, uid, ids, context=context)

    def _selection_invoice_type(self, cr, uid, ids, context=None):
        """selection 字段的定义一种方式  这样就可以方便的修改selection 字段的值"""
        return [
            (u'不含票', u'不含票'),
            (u'含增值税发票', u'含增值税发票'),
            (u'含普票', u'含普票')
        ]

    def action_cancel(self, cr, uid, ids, context=None):
        for purchase in self.browse(cr, uid, ids, context=context):
            picking_obj = self.pool.get('stock.picking')
            if purchase.is_direct_delivery:
                picking_id = picking_obj.search(cr, uid, [('direct_delivery_po_id', '=', purchase.id),
                                                              ('state', 'not in', ('cancel', 'done'))], context=context)
                if picking_id:
                    picking_obj.action_cancel(cr, uid, picking_id, context=context)
                    picking_obj.unlink(cr, uid, picking_id, context=context)

            for line in purchase.order_line:
                if line.incomed_qty != 0:
                    raise osv.except_osv(
                                        _('Unable to cancel the purchase order %s.') % (purchase.name),
                                        _('You have already received some goods for it.  '))
            self.pool.get('stock.picking').action_cancel(cr, uid, [x.id for x in purchase.picking_ids if
                                                                   x.state not in ('cancel', 'done')], context=context)
            for inv in purchase.invoice_ids:
                if inv and inv.state not in ('cancel', 'draft'):
                    raise osv.except_osv(
                        _('Unable to cancel this purchase order.'),
                        _('You must first cancel all invoices related to this purchase order.'))
            self.pool.get('account.invoice') \
                .signal_workflow(cr, uid, map(attrgetter('id'), purchase.invoice_ids), 'invoice_cancel')
        self.signal_workflow(cr, uid, ids, 'purchase_cancel')
        return True

        # picking_obj = self.pool.get('stock.picking')
        # for purchase in self.browse(cr, uid, ids, context=context):
        #     if purchase.is_direct_delivery:
        #         picking_id = picking_obj.search(cr, uid, [('direct_delivery_po_id', '=', purchase.id),
        #                                                   ('state', 'not in', ('cancel', 'done'))], context=context)
        #         if picking_id:
        #             picking_obj.action_cancel(cr, uid, picking_id, context=context)
        #             picking_obj.unlink(cr, uid, picking_id, context=context)
        # return super(purchase_order, self).action_cancel(cr, uid, ids, context=context)

    # 查看相关发票
    def view_invoice_ext(self, cr, uid, ids, context=None):
        if context is None:
            context = {}
        mod_obj = self.pool.get('ir.model.data')
        dummy, action_id = tuple(mod_obj.get_object_reference(
            cr, uid, 'account', 'action_invoice_tree2'))
        action = self.pool.get('ir.actions.act_window').read(
            cr, uid, action_id, context=context)
        # 获取发票的ID
        invoice_ids = []
        for po in self.browse(cr, uid, ids, context=context):
            invoice_ids += [invoice.id for invoice in po.invoice_ids]
        # 将context的值置空
        action['context'] = {}
        if len(invoice_ids) > 1:
            action[
                'domain'] = "[('id','in',[" + ','.join(map(str, invoice_ids)) + "])]"
        else:
            res = mod_obj.get_object_reference(
                cr, uid, 'account', 'invoice_supplier_form')
            action['views'] = [(res and res[1] or False, 'form')]
            action['res_id'] = invoice_ids and invoice_ids[0] or False
        return action

    # 勾选“采购已核对”之后，自动修改采购核对时间
    def button_purchase_checked(self, cr, uid, ids, context=None):
        if self.search(cr, uid, ['&', ('id', 'in', ids), ('is_purchase_checked', '=', False)], context=context):
            self.write(cr, uid, ids, {'is_purchase_checked': True,
                                      'purchaser_check_time': datetime.now(), }, context=context)

    # 将当前采购单中的可以入库的采购订单明细，写入入库助手向导明细中，然后展示在入库助手向导中
    def picking_wizard(self, cr, uid, ids, context=None):
        order_line_obj = self.pool.get('purchase.order.line')
        picking_line_obj = self.pool.get('purchase.picking.line')
        picking_wizard_obj = self.pool.get('purchase.picking.wizard')
        pol_ids = []
        picking_line_ids = []
        if ids:
            cr.execute(
                "SELECT id FROM purchase_order_line WHERE order_id=%s AND (abs(product_qty)>incomed_qty OR delivered_qty IS NULL)", (ids[0],))
            pol_ids = [row[0] for row in cr.fetchall()]
        # 将符合条件的采购订单明细记录，写入向导明细
        if pol_ids:
            for order_line in order_line_obj.browse(cr, uid, pol_ids, context=context):
                picking_line_data = {'product_id': order_line.product_id.id,
                                     'partner_id': order_line.partner_id.id,
                                     'po_location_id':order_line.order_id.location_id.id,
                                     'purchase_order_line_id': order_line.id,# #1050 采购助手的入库向导填写入库库位时只过滤出采购单的库位和下级库位 [Issue开启]
                                     'order_qty': order_line.product_qty,
                                     'incomed_qty': order_line.incomed_qty,
                                     'delivered_qty': order_line.delivered_qty}
                picking_line_id = picking_line_obj.create(
                    cr, uid, picking_line_data, context=context)
                picking_line_ids.append(picking_line_id)
            # 将向导明细写入向导，并返回向导ID
            if picking_line_ids:
                picking_wizard_data = {}
                picking_wizard_data.update({'picking_line': map(
                    lambda x: (4, x, False), picking_line_ids)})
                wizard_id = picking_wizard_obj.create(
                    cr, uid, picking_wizard_data, context=context)
                return {
                    'type': 'ir.actions.act_window',
                    'res_model': 'purchase.picking.wizard',
                    'view_mode': 'form',
                    'view_type': 'form',
                    'name': u'入库助手向导',
                    'target': 'new',
                    'res_id': wizard_id,
                    'context': {'purchase_order_id': ids},
                }
        else:
            raise osv.except_osv(u'错误', u'已生成入库单，请点击"相关入库单" 按钮进行查看和操作！')

    # 根据so_ids将销售订单的名字拼接
    def _compute_so_code_list(self, cr, uid, ids, name, arg, context=None):
        res = {}
        for po in self.browse(cr, uid, ids, context=context):
            if po.so_ids:
                code_list = []
                so_list=[]
                if po.so_ids!=u'无':
                    so_list = eval(po.so_ids)
                for so in self.pool.get("sale.order").browse(cr, uid, so_list, context=context):
                    code_list.append(so.name)
                res.update({po.id: '、'.join(code_list)})
            else:
                res.update({po.id: u'无'})
        return res
    
    def handle_purchase_picking(self, cr, uid, purchase, context=None):
        if purchase.picking_ids:
            delete_pick_list = []
            delete_move_list = []
            move_obj = self.pool.get('stock.move')
            for picking in purchase.picking_ids:
                not_done_move_list = [line.id for line in picking.move_lines if line.state != 'done']
                if len(not_done_move_list) == len(picking.move_lines):
                    delete_pick_list.append(picking.id)
                else:
                    delete_move_list.extend(not_done_move_list)
            if delete_move_list:
                for move_row in move_obj.browse(cr, uid, delete_move_list, context=context):
                    move_row.purchase_line_id.write({'delivered_qty': move_row.purchase_line_id.delivered_qty -
                                                                      move_row.product_qty})
                move_obj.action_cancel(cr, uid, delete_move_list, context=context)
                move_obj.unlink(cr, uid, delete_move_list, context=context)
            if delete_pick_list:
                self.pool.get('stock.picking').unlink(cr, uid, delete_pick_list, context=context)
        return True
    
    # 点击"申请修改"按钮
    def button_modify_order(self, cr, uid, ids, context=None):
        picking_obj = self.pool.get('stock.picking')
        # 采购单的开票类型为“基于生成的发票草稿”，且已存在发票，给出提示
        for purchase in self.browse(cr, uid, ids, context):
            if purchase.invoice_method == 'order':
                invoice_id = self.pool.get('account.invoice').search(
                    cr, uid, [('origin', '=', purchase.name)], context=context)
                if invoice_id:
                    raise osv.except_osv(u'警告', u'基于整单开票的采购单不能申请修改！')
            self.handle_purchase_picking(cr, uid, purchase, context=context)
            if purchase.picking_ids:
                self.write(cr, uid, ids, {
                           'is_partner_alterable': True}, context=context)
            if purchase.invoice_ids:
                raise osv.except_osv(u'错误', u'采购订单已开发票不允许修改。')
            if purchase.state =='draft':
                raise osv.except_osv(u'错误', u'采购订单已在草稿状态，可以进行修改，请刷新后再进行操作！')
            if purchase.state in ('except_picking', 'except_invoice', 'cancel'):
                raise osv.except_osv(u'错误', u'采购订单可能存在出库异常、发票异常、已取消等原因，不能申请修改。')
            if purchase.state == 'done':
                raise osv.except_osv(u'错误', u'采购订单已经入库完成，不能申请修改')
            if purchase.is_direct_delivery:
                picking_id = picking_obj.search(cr, uid, [('direct_delivery_po_id', '=', purchase.id)], context=context)
                if picking_id:
                    picking_obj.action_cancel(cr, uid, picking_id, context=context)
                    picking_obj.unlink(cr, uid, picking_id, context=context)
        self.signal_workflow(cr, uid, ids, 'modify_order')

        self.message_post(cr, uid, ids, u'采购单申请修改',
                          subtype='mail.mt_comment', context=context)
        order_ids = self.pool.get("purchase.order.line").search(cr, uid, [('order_id', 'in', ids)], context=context)
        self.pool.get("purchase.order.line").write(cr, uid, order_ids, {'state': 'draft'}, context=context)
        return self.write(cr, uid, ids, {'state': 'draft'}, context=context)

    # 计算“未生成发票金额”字段，用采购单上的amount_total-所有对应发票的amount_total的和，通过第三方表purchase_invoice_rel实现
    def _compute_sum_uninvoiced(self, cr, uid, ids, name, arg, context=None):
        res = {}
        for purchase in self.browse(cr, uid, ids, context=context):
            no_invoiced_moeny = 0
            for order_line in purchase.order_line:
                no_invoiced_moeny = 0
                if not order_line.invoiced:
                    no_invoiced_moeny = no_invoiced_moeny + order_line.price_subtotal
            res.update({purchase.id: no_invoiced_moeny})
        return res

    # 计算“发票未申请付款金额”字段,找到采购单对应的发票中voucher_id为空的amount_total总和
    def _compute_sum_no_apply(self, cr, uid, ids, name, arg, context=None):
        res = {}
        po_ids = ','.join(map(str, ids))
        cr.execute('''SELECT  po.id AS po_id,
                              sum(ai.amount_total) AS sum_no_apply
                      FROM purchase_order AS po
                      INNER JOIN purchase_invoice_rel AS pir ON po.id=pir.purchase_id
                      INNER JOIN account_invoice AS ai ON pir.invoice_id=ai.id
                      WHERE ai.voucher_id IS NULL AND po.id in (%s)
                      GROUP BY po.id
        ''' % (po_ids))
        for query_data in cr.dictfetchall():
            res.update({query_data['po_id']: query_data['sum_no_apply']})
        return res

    # 重载purchase_order_lodop的print_quotation_lodop方法，让其符合采购单的workflow流程
    def print_quotation_lodop(self, cr, uid, ids, context=None):
        for po in self.browse(cr, uid, ids, context=context):
            if po.state == 'draft':
                self.write(cr, uid, ids, {
                           'is_print_quotation': True}, context=context)
                self.signal_workflow(cr, uid, ids, 'send_rfq')
        return super(purchase_order, self).print_quotation_lodop(cr, uid, ids, context=context)

    # 重载odoo的print_quotation方法
    def print_quotation(self, cr, uid, ids, context=None):
        for po in self.browse(cr, uid, ids, context=context):
            if po.state == 'draft':
                self.write(cr, uid, ids, {
                           'is_print_quotation': True}, context=context)
        return super(purchase_order, self).print_quotation(cr, uid, ids, context=context)

    # 选择联系人后相应修改对应的联系方式
    def onchange_contacts(self, cr, uid, ids, contacts_id, context=None):
        # 去除掉customer_address，替换成company_contacts
        res = {}
        if contacts_id:
            contacts = self.pool.get('customer.address').browse(cr, uid,
                                                                contacts_id, context=context)
            if contacts.street:
                address = contacts.street if not contacts.county_id.county_name or contacts.county_id.county_name in contacts.street else "%s %s" % (contacts.county_id.county_name, contacts.street)
            else:
                address = ''
            supplier_address = '%s %s %s' % (address, contacts.partner_id.name, contacts.phone)
            res.update({'value': {
                'mobile_number': contacts.phone,
                'fax_number': contacts.fax,
                'supplier_address': supplier_address,
            }})
        else:
            res.update({'value': {
                'phone_number': '',
                'mobile_number': '',
                'fax_number': '',
                'supplier_address': '',
            }})
        return res

    # 重载odoo的供应商上的onchange,为当前询价单的供应商赋默认联系人
    def onchange_partner_id(self, cr, uid, ids, partner_id, context=None):
        res = super(purchase_order, self).onchange_partner_id(
            cr, uid, ids, partner_id, context=context)
        if partner_id:
            # 去除掉customer_address，替换成company_contacts
            contacts_id = self.pool.get('customer.address').search(cr, uid, [('partner_id', '=', partner_id),
                                                                             ('default', '=', True)], context=context)
            if contacts_id:
                res['value'].update({'contacts_id': contacts_id[0]})
            else:
                res['value'].update({'contacts_id':False,
                                    'phone_number': '',
                                     'mobile_number': '',
                                     'fax_number': '',
                                     'supplier_address': '', })
            partner_row = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context)
            res['value'].update({'invoice_method': partner_row.invoice_method or 'manual'})
        return res

    # 重载取得入库单(查看相关入库单按钮有用到, 修改为可以看到相关退货单)
    def _get_picking_ids(self, cr, uid, ids, field_names, args, context=None):
        res = {}
        for po_id in ids:
            res[po_id] = []
        query = "SELECT id, po_id FROM stock_picking WHERE state <> 'cancel' and po_id in %s"
        cr.execute(query, (tuple(ids), ))
        picks = cr.fetchall()
        for pick_id, po_id in picks:
            res[po_id].append(pick_id)
        return res
    # 写一个purchase_order的is_paid字段，实现类似sale_order的invoiced字段的功能。当该采购单付款完成时，值为True

    def _is_paid(self, cr, uid, ids, field, args, context=None):
        res = {}
        for purchase in self.browse(cr, uid, ids, context=context):
            res[purchase.id] = True
            invoice_existence = False
            for invoice in purchase.invoice_ids:
                if invoice.state != 'cancel':
                    invoice_existence = True
                    if invoice.state != 'paid':
                        res[purchase.id] = False
                        break
            if not invoice_existence or purchase.state == 'manual':
                res[purchase.id] = False
        return res

    def _invoiced_search(self, cr, uid, ids, field, args, context=None):
        """这个是 invoiced 字段的的search的方法(function 字段)"""
        if not len(args):
            return []
        clause = ''
        purchase_clause = ''
        no_invoiced = False
        for arg in args:
            if (arg[1] == '=' and arg[2]) or (arg[1] == '!=' and not arg[2]):
                clause += 'AND inv.state = \'paid\''
            else:
                clause += 'AND inv.state != \'cancel\' AND sale.state != \'cancel\'  AND inv.state <> \'paid\'  AND rel.purchase_id = purchase.id '
                purchase_clause = ',  purchase_order AS purchase '
                no_invoiced = True

        cr.execute('SELECT rel.purchase_id '
                   'FROM purchase_invoice_rel AS rel, account_invoice AS inv ' + purchase_clause +
                   'WHERE rel.invoice_id = inv.id ' + clause)
        res = cr.fetchall()
        if no_invoiced:
            cr.execute('SELECT purchase.id '
                       'FROM purchase_order AS purchase '
                       'WHERE purchase.id NOT IN '
                       '(SELECT rel.order_id '
                       'FROM purchase_invoice_rel AS rel) and purchase.state != \'cancel\'')
            res.extend(cr.fetchall())
        if not res:
            return [('id', '=', 0)]
        return [('id', 'in', [x[0] for x in res])]

    def _compute_pick_state_completion(self, cr, uid, ids, fields, args, context=None):
        res = {}
        if not ids:
            return res
        stock_picking_obj = self.pool.get('stock.picking')
        for order in self.browse(cr, uid, ids, context=context):
            picking_state = True if len(order.order_line) else False
            for order_line in order.order_line:
                if order_line.incomed_qty != abs(order_line.product_qty):
                    picking_state = False
                    break

            if picking_state:
                picking_id = stock_picking_obj.search(
                    cr, uid, [('po_id', '=', order.id), ('state', '!=', 'cancel')], context=context)
                for picking in stock_picking_obj.browse(cr, uid, picking_id, context=context):
                    if picking.state == 'done':
                        picking_state = True
                    else:
                        picking_state = False
                        break

            if picking_state:
                res[order.id] = {'pick_state': picking_state,
                                 'income_finished_time': datetime.today()}
            else:
                res[order.id] = {'pick_state': picking_state,
                                 'income_finished_time': False}
            if order.invoiced and res[order.id].get('pick_state'):
                order.write({'state': 'done'})
        return res

    def _invoiced(self, cursor, user, ids, name, arg, context=None):
        res = {}
        for purchase in self.browse(cursor, user, ids, context=context):
            res[purchase.id] = all(line.invoiced for line in purchase.order_line if line.state != 'cancel')
            if purchase.pick_state and all([line.invoiced for line in purchase.order_line if line.state != 'cancel']
                                           or [False]):
                purchase.write({'state': 'done'})
            else:
                if purchase.state == 'done':
                    purchase.write({'state': 'approved'})
        return res

    def _determine_pick_state_completion(self, cr, uid, ids, context=None):
        res = {}
        if not ids:
            return res
        for order_line in self.pool.get('purchase.order.line').browse(cr, uid, ids, context=context):
            if order_line.incomed_qty == abs(order_line.product_qty):
                res[order_line.order_id.id] = True
        return res.keys()

    def _get_moving_picking(self, cr, uid, ids, context=None):
        res = {}
        if not ids:
            return res
        for picking in self.pool.get('stock.picking').browse(cr, uid, ids, context=context):
            res[picking.po_id.id] = True
        return res.keys()

    def _get_last_arrive_date(self, cr, uid, ids, fields, args, context=None):
        '''计算采购订单对应采购订单行最晚到达日期。首先把订单行所有计划日期加入列表，然后对该列表排序，最后取最晚到达日期'''
        res = {}
        delivery_time_list = []
        for purchase in self.browse(cr, uid, ids, context=context):
            if purchase.order_line:
                for line in purchase.order_line:
                    delivery_time_list.append(line.date_planned)

                delivery_time_list.sort()
                res[purchase.id] = delivery_time_list[-1]
            else:
                res[purchase.id] = False
        return res

    def _get_incoming_picking(self, cr, uid, ids, fields, args, context):
        '''计算是否全部生成入库单，如果已建发货数量的值等于数量，则已全部生成入库单
        1.产品数量为零不需要生成。
        2.产品数量为正，则产品数量和 。
        3.产品数量为负，则产品数量的绝对值和 已建入库单数量相同。
        4.产品类型如果是服务类，则不用生成出入库单。
        '''
        res = {}
        for purchase in self.browse(cr, uid, ids, context=context):
            for line in purchase.order_line:
                if line.product_qty == 0 \
                        or (line.product_qty > 0 and line.delivered_qty == line.product_qty)\
                        or (line.product_qty < 0 and line.delivered_qty == abs(line.product_qty))\
                        or line.product_id.type == 'service':
                    res[purchase.id] = True
                else:
                    res[purchase.id] = False
        return res

    def on_change_is_direct_delivery(self, cr, uid, ids, is_direct_delivery, so_ids, context=None):
        if so_ids == '[]' and is_direct_delivery:
            return {'value': {'is_direct_delivery': '', 'develiey_address': ''},
                    'warning': {'title': u'错误', 'message': u'采购单只能关联一个或多个销售订单的情况下才能直送，请在更多菜单里进行采购单关联销售单的操作'}}
        if is_direct_delivery:
            if len(eval(so_ids)) > 1:
                if len(set(["%s%s%s%s%s%s%s%s" % (order.partner_id.id, order.province_id.id, order.consignee,
                                                  order.mobile_number, order.phone_number, order.street,
                                                  order.city_id.id, order.county_id.id) for order in
                            self.pool.get('sale.order').browse(cr, uid, eval(so_ids), context=context)])) > 1:
                    return {'value': {'is_direct_delivery': ''},
                        'warning': {'title': u'错误', 'message': u'多个销售订单的直送需要同一个客户相同送货地址，不同客户和地址的直送请分开下采购单'}}
            else:
                sale_order_objs = self.pool.get('sale.order').browse(cr, uid, eval(so_ids), context=context)[0]
                if sale_order_objs.county_id and sale_order_objs.street and sale_order_objs.county_id.county_name not in sale_order_objs.street:
                    address = "%s %s" % (sale_order_objs.county_id.county_name, sale_order_objs.street)
                    develiey_address = "%s %s  %s    %s" % (address, sale_order_objs.consignee, sale_order_objs.mobile_number,
                                         sale_order_objs.phone_number)
                else:
                    develiey_address = sale_order_objs.street
                return {'value': {'develiey_address': develiey_address,
                                  'planned_delivery_date': sale_order_objs.expected_delivery_time}
                        }
        else:
            return {'value': {'is_direct_delivery': False, 'develiey_address': False}}

    def _get_delivered_qty_change(self, cr, uid, ids, context=None):
        # 根据采购订单行找到采购订单
        result = {}
        for purchase_line in self.pool.get('purchase.order.line').browse(cr, uid, ids, context=context):
            result[purchase_line.order_id.id] = True
        return result.keys()

    def onchange_shipping_method(self, cr, uid, ids, shipping_method, context=None):
        """ 采购单form送货方式字段标签文字改为“常用送货地址”，
        增加一个字段在下面：“送货地址”develiey_address，常用送货地址选择后填充送货地址develiey_address [Issue开启]"""
        shipping_method_objs = self.pool.get('shipping.method').browse(cr, uid, shipping_method, context=context)
        if shipping_method_objs:
            return {'value': {
                'develiey_address': shipping_method_objs[0].name
            }}
        
    def _get_so_min_delivery_time(self, cr, uid, ids, name, args, context=None):
        """
        根据采购单关联的销售订单找出最近的发货日期
        :return:
        """
        res = {}
        for purchase_order_row in self.browse(cr, uid, ids, context=context):
            if purchase_order_row.so_code_list != u'无' and purchase_order_row.so_ids:
                so_ids = eval(purchase_order_row.so_ids or '[]')
                delivery_time_list = [sale_order_row.expected_delivery_time
                for sale_order_row in self.pool.get('sale.order').browse(cr, uid, so_ids, context=context)]
                res[purchase_order_row.id] = min(delivery_time_list) if delivery_time_list else u'无'
            else:
                res[purchase_order_row.id] = u'无'
        return res
    def _get_purchase_order_id(self, cr, uid, ids, context=None):
        """
        如果销售订单的发货日期有变化，需要同步修改采购单的这个字段值（先通过销售订单查到采购单）
        :return:
        """
        purchase_ids = []
        for sale_row in self.browse(cr, uid, ids, context=context):
            purchase_ids.extend(self.pool.get('purchase.order').search(cr, uid,
                                                                       [('so_code_list', 'ilike', sale_row.name)],
                                                                       context=context))
        return purchase_ids

    def compute_change_planned_date_time(self, cr, uid, ids, name, args, context=None):
        history_obj = self.pool.get('value.history')
        res = {}
        for purchase_order in self.browse(cr, uid, ids, context=context):
            change_time = history_obj.search_count(cr, uid, [('model_name', 'in', ('purchase.order', 'purchase.order.line')),
                                                             ('field_name', 'in', ('planned_delivery_date', 'expected_delivery_time')),
                                                             '|', ('record_id', '=', purchase_order.id),
                                                             ('record_id', 'in', [line.id for line in purchase_order.order_line])
                                                             ], context=context)
            res[purchase_order.id] = change_time
        return res

    def compute_change_planned_date_html(self, cr, uid, ids, name, args, context=None):
        history_obj = self.pool.get('value.history')
        res = {}

        for purchase_order in self.browse(cr, uid, ids, context=context):
            change_records = history_obj.search_read(cr, uid, [('model_name', 'in',
                                                                ('purchase.order', 'purchase.order.line')),
                                                                ('field_name', 'in',
                                                                ('planned_delivery_date', 'expected_delivery_time')),
                                                               '|', ('record_id', '=', purchase_order.id),
                                                               ('record_id', 'in', [line.id for line in purchase_order.order_line])
                                                               ],
                                                             ['modify_user_id', 'old_val', 'new_value', 'model_name'],
                                                     context=context)
            records_table = table(cl="oe_list_content")
            tr_index, td_class, td_index = 0, 'oe_list_field_cell oe_list_field_selection', 0
            curent_tr = records_table << tr(id=str(tr_index))
            curent_tr << th(id=str(td_index), cl=td_class) << "修改人"
            td_index += 1
            curent_tr << th(id=str(td_index), cl=td_class) << "类型"
            td_index += 1
            curent_tr << th(id=str(td_index), cl=td_class) << "原记录值"
            td_index += 1
            curent_tr << th(id=str(td_index), cl=td_class) << "修改后值"
            for record in change_records:
                tr_index += 1
                td_index = 0
                curent_tr = records_table << tr(id=str(tr_index))
                curent_tr << td(id=str(td_index), cl=td_class) << record.get('modify_user_id', [False, False])[1] or ''
                td_index += 1
                model_name = record.get('model_name') == 'purchase.order' and u'采购单' or u'采购订单行'
                curent_tr << td(id=str(td_index), cl=td_class) << model_name
                td_index += 1
                curent_tr << td(id=str(td_index), cl=td_class) << record.get('old_val') or ''
                td_index += 1
                curent_tr << td(id=str(td_index), cl=td_class) << record.get('new_value') or ''
            res[purchase_order.id] = records_table.render()
        return res

    def on_change_is_shop_direct(self, cr, uid, ids, is_shop_direct, is_shop_order, context=None):
        model_data_obj = self.pool.get('ir.model.data')
        if is_shop_order:
            if not is_shop_direct:
                picking_type_id = model_data_obj.xmlid_to_res_id(
                    cr, SUPERUSER_ID, 'dftg_ext.stock_picking_type_stock_location_mall_internal')
            else:
                picking_type_id = model_data_obj.xmlid_to_res_id(
                    cr, SUPERUSER_ID, 'dftg_ext.stock_picking_type_stock_location_mall_payroll')

            return {'value': {'picking_type_id': picking_type_id}}
        return {}

    _columns = {
        'pick_state': fields.function(_compute_pick_state_completion, string='入库完成状态', type='boolean', store={
            'stock.picking': (_get_moving_picking, ['state'], 16),
            'purchase.order.line': (_determine_pick_state_completion, ['incomed_qty', 'product_qty'], 16),
        }, multi='sums'),
        'so_ids': fields.char(string=u'销售订单ID'),
        'so_code_list': fields.function(_compute_so_code_list, string=u'销售订单列表', type='text',
                                        store=True, copy=False),
        'so_min_delivery_time': fields.function(_get_so_min_delivery_time, string='销售最近发货日期', type='char',
                                                store={'purchase.order': (lambda self, cr, uid, ids, context=None:
                                                                          ids, ['so_ids'], 10),
                                                       'sale.order': (_get_purchase_order_id,
                                                                      ['expected_delivery_time'],10)}),
        'plan_arrive_date': fields.function(_get_last_arrive_date, type='date', string='计划到达日期'),
        'planned_delivery_date': fields.date(u'计划到货日期', track_visibility='onchange'),
        'is_print_price': fields.boolean(string=u'是否打印价格'),
        'print_state': fields.boolean(string=u'打印状态'),
        'shipping_method': fields.many2one('shipping.method', domain=[('is_active', '=', True)], string=u'常用送货地址'),
        'invoice_type': fields.selection('_selection_invoice_type', string=u'发票类型'),
        'stock_memo': fields.char(string=u'仓库备注'),
        'is_purchase_checked': fields.boolean(string=u'采购已核对'),
        'purchaser_check_time': fields.datetime(string=u'采购核对时间'),
        'sum_uninvoiced': fields.function(_compute_sum_uninvoiced, string=u'未生成发票金额', type='float'),
        'sum_no_apply': fields.function(_compute_sum_no_apply, string=u'发票未申请付款金额', type='float'),
        'income_finished_time': fields.function(_compute_pick_state_completion, type='datetime', string=u'入库完成时间', store={
            'stock.picking': (_get_moving_picking, ['state'], 16),
            'purchase.order.line': (_determine_pick_state_completion, ['incomed_qty', 'product_qty'], 26),
        }, multi='sums'),
        'advance_payment': fields.float(string=u'预付款'),
        'voucher_id': fields.many2one('account.voucher', u'预付款申请付款编号'),
        # 'contacts': fields.many2one('customer.address', string=u'联系人'),
        # 去除掉customer_address，替换成company_contacts
        'contacts_id': fields.many2one('customer.address', string=u'联系人'),
        'phone_number': fields.char(string=u'电话号码'),
        'mobile_number': fields.char(string=u'手机号码'),
        'fax_number': fields.char(string=u'传真'),
        'picking_ids': fields.function(_get_picking_ids, method=True, type='one2many', relation='stock.picking', string='Picking List', help="This is the list of receipts that have been generated for this purchase order."),
        'is_paid': fields.function(_is_paid, string='已付款', fnct_search=_invoiced_search, type='boolean'),
        'is_partner_alterable': fields.boolean(u'能否更改供应商', copy=False),
        'is_picking_button_assigned': fields.boolean(u'是否点击入库（准备移动）按钮'),
        'is_incoming_picking_done': fields.function(_get_incoming_picking, type='boolean', string='是否全部生成入库单', store={
            'purchase.order.line': (_get_delivered_qty_change, ['delivered_qty'], 10)}),
        'is_direct_delivery': fields.boolean(u'是否供应商直送', copy=False),
        'is_return': fields.boolean(u'退货单', copy=False, select=True),
        'is_exchange': fields.boolean(u'换货单', copy=False, select=True),
        'sale_order_change_ids': fields.one2many('sale.order.line.change', 'po_id', string='订单变化表列表'),
        'is_flagged_one': fields.boolean(u'处理标记', copy=False),
        'develiey_address': fields.char(u'送货地址'),
        'is_self_pickup': fields.boolean(u'是否自提'),
        'supplier_address': fields.char(u'提货地址'),
        'order_line': fields.one2many('purchase.order.line', 'order_id', 'Order Lines',
                                      states={'approved':[('readonly',False)],
                                              'done':[('readonly',False)]},
                                      copy=True),
        'is_manual_create_stock': fields.boolean(u'手动生成入(出)库单', copy=False),
        'is_card_manufacture': fields.boolean(u'卡券制作', copy=False),# 暂时不用·
        'is_card_product_purchase': fields.boolean(u'卡券产品采购', copy=False),
        'change_planned_date_time': fields.function(compute_change_planned_date_time, type='integer', readonly=1, string='修改次数'),
        'change_planned_date_html': fields.function(compute_change_planned_date_html, type='html', readonly=1,
                                                    string='到货日期修改记录'),
        'is_shop_order': fields.boolean(u'是商城订单'),
        'is_shop_direct': fields.boolean(u'一件代发'),
        'invoiced': fields.function(_invoiced, string='Invoice Received', type='boolean', copy=False,
                                    help="It indicates that an invoice has been validated"),
    }

    def default_get(self, cr, uid, fields, context=None):
        res = super(purchase_order, self).default_get(
            cr, uid, fields, context=context)
        shipping_method_id = self.pool.get("shipping.method").search(cr, uid, [('is_default', '=', True)], context=context)
        if shipping_method_id:
            shipping_method_row = self.pool.get("shipping.method").browse(cr, uid, shipping_method_id[0], context=context)
            res.update({'shipping_method': shipping_method_id[0],'develiey_address': shipping_method_row.name})
        if context.get('default_is_shop_order') or res.get('is_shop_order'):
            model_data_obj = self.pool.get('ir.model.data')
            pick_type_id = model_data_obj.xmlid_to_res_id(
                cr, SUPERUSER_ID, 'dftg_ext.stock_picking_type_stock_location_mall_internal')
            res.update({'picking_type_id': pick_type_id})
        return res
    _defaults = {
        'pick_state': False,
        'is_print_price': False,
        'is_purchase_checked': False,
        'invoice_method': 'picking',
        'is_apply_advance_payment': False,
        'advance_payment': 0,  # 预付款金额默认为0，否则付款申请会报js格式化错误
        'is_partner_alterable': False,
        'is_picking_button_assigned': False,
        'so_ids': '[]',
    }


class purchase_order_advance_payment_wizard(osv.osv):
    _name = "purchase.order.advance.payment.wizard"
    _description = u"采购单预付款申请向导"

    # 确认按钮
    def confirm_button(self, cr, uid, ids, context=None):
        purchase_order_obj = self.pool.get('purchase.order')
        account_voucher_obj = self.pool.get('account.voucher')
        account_account_obj = self.pool.get('account.account')
        account_account_id = account_account_obj.search(
            cr, uid, [('code', '=', '2202')], context=context)

        # 查找“未指定付款方式”，若没有则新建一个
        journal_ids = self.pool.get('account.journal').search(
            cr, uid, [('code', '=', 'CGXN')], context=context)
        if not journal_ids:
            journal_id = self.pool.get('account.journal').create(
                cr, uid, {'code': 'CGXN', 'name': u'采购虚拟账户', 'type': 'bank'}, context=context)
        else:
            journal_id = journal_ids[0]

        # 判断预付款金额是否为0，为0不能提交申请
        for wizard in self.browse(cr, uid, ids, context=context):
            for wizard_line in wizard.advance_payment_line:
                if not wizard_line.payment_amount:
                    raise osv.except_osv(
                        u'错误', u'%s可用预付款金额为0，不能提交申请' % (wizard_line.id))

                # 计算关联的采购单的单据，用“,”分割
                po_ids = eval(wizard_line.po_ids)
                po_codes = ''
                for po in purchase_order_obj.browse(cr, uid, po_ids, context=context):
                    if po_codes:
                        po_codes += ','
                    po_codes += po.name
                # 为创建凭证的字段赋值
                voucher_data = {
                    'partner_id': wizard_line.partner_id.id,
                    'date': date.today(),
                    'amount': wizard_line.payment_amount,
                    'journal_id': journal_id,
                    'narration': po_codes + u'预付款',
                    'account_id': account_account_id[0],  # 会计科目
                    'type': 'purchase',
                }
            voucher_id = account_voucher_obj.create(
                cr, uid, voucher_data, context=context)
            # 把凭证id写回采购单的voucher_id
            for voucher in account_voucher_obj.browse(cr, uid, voucher_id, context=context):
                voucher_number = voucher.narration[0:12]
                if isinstance(voucher_number, unicode):
                    voucher_number = voucher_number.encode('gb2312').split(',')

                purchase_ids = purchase_order_obj.search(
                    cr, uid, [('name', 'in', voucher_number)], context=context)
                purchase_order_obj.write(cr, uid, purchase_ids, {
                                         'voucher_id': voucher_id}, context=context)

        return True

    # 重载向导的default_get方法，初始化数据
    def default_get(self, cr, uid, fields, context=None):
        res = super(purchase_order_advance_payment_wizard, self).default_get(
            cr, uid, fields, context=context)
        active_ids = context.get('active_ids')
        purchase_order_obj = self.pool.get('purchase.order')

        before_partner = ''
        for purchase_order in purchase_order_obj.browse(cr, uid, active_ids, context=context):
            # 如果采购单的voucher_id存在，则取消该采购单的预付款合并
            if purchase_order.voucher_id:
                if len(active_ids) > 1:
                    active_ids.remove(purchase_order.id)
                else:
                    raise osv.except_osv(u'警告', u'该采购单已经申请过预付款')
            # 判断勾选的采购单的供应商是否相同
            if before_partner:
                if before_partner != purchase_order.partner_id:
                    raise osv.except_osv(u'警告', u'您不能合并不同供应商的预付款')
            before_partner = purchase_order.partner_id

        if len(active_ids) > 1:
            operation = 'in %s' % (str(tuple(active_ids)))
        else:
            operation = '= %d' % (active_ids[0])

        cr.execute("""select po.partner_id as partner_id,
                            array_agg(po.id) as po_ids,
                            sum(po.advance_payment) as payment_amount,
                            null as account_id
                            from purchase_order as po
                            where po.id %s
                            group by po.partner_id""" % (operation))

        line_data = cr.dictfetchall()
        res.update({'advance_payment_line': line_data})
        return res

    _columns = {
        'advance_payment_line': fields.one2many('purchase.order.advance.payment', 'payment_id', u'采购单预付款'),
    }


class purchase_order_advance_payment(osv.osv):
    _name = "purchase.order.advance.payment"
    _description = u"采购单预付款申请"

    _columns = {
        'payment_id': fields.many2one('purchase.order.advance.payment.wizard', string='预付款向导ID'),
        'payment_amount': fields.float(u'预付款金额'),
        'po_ids': fields.char(string=u'采购单'),
        'partner_id': fields.many2one('res.partner', u'供应商'),
        'account_id': fields.many2one('account.account', string=u'会计科目ID'),
    }


class shipping_method(osv.osv):
    # 该表的设置权限给采购人员
    _name = 'shipping.method'
    _description = u'采购单中的送货方式'

    def create(self, cr, uid, vals, context=None):
        """ is_default 控制是否是 默认的运输方式 并且保证默认送货方式只有一个"""
        if vals.get("is_default", False):
            shipping_method_id = self.search(cr, uid, [('is_default', '!=', False)], context=context)
            if shipping_method_id:
                self.write(cr, uid, shipping_method_id, {'is_default': False}, context=context)
        return super(shipping_method, self).create(cr, uid, vals, context=context)

    def write(self, cr, uid, ids, vals, context=None):
        """ is_default 控制是否是 默认的运输方式 并且保证默认送货方式只有一个"""
        if vals.get("is_default", False):
            shipping_method_id = self.search(cr, uid, [('is_default', '!=', False)], context=context)
            if shipping_method_id:
                self.write(cr, uid, shipping_method_id, {'is_default': False}, context=context)
        return super(shipping_method, self).write(cr, uid, ids, vals, context=context)
    _columns = {
        'name': fields.char(string=u'送货方式'),
        'is_active': fields.boolean(string=u"是否启用"),
        'is_default': fields.boolean(u'是否默认', copy=False),
    }

    _defaults = {
        'is_active': True,
    }


class purchase_picking_wizard(osv.osv_memory):
    # 入库助手向导，列出未入库的产品(功能还为完善)
    _name = 'purchase.picking.wizard'
    _description = u'入库助手向导'

    def fields_view_get(self, cr, uid, view_id=None, view_type='form',
                        context=None, toolbar=False, submenu=False):
        # 对选取的采购单进行条件的判断 采购单不存在采购单行则不允许操作 ，采购单 没有确定 就不能生成入库单
        if context is None:
            context = {}
        res = super(purchase_picking_wizard, self).fields_view_get(
            cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=False)
        return res


    def insert_stock_move(self, cr, uid, wizard_id, picking_id, order, context=None):
        """生成入(出)库单行的方法"""
        stock_move_obj = self.pool.get('stock.move')
        picking_wizard = self.browse(cr, uid, wizard_id, context=context)
        move_lines = []
        move_line_in_data, move_line_out_data = {}, {}
        stock_picking_type_obj = self.pool.get("stock.picking.type")
        model_data_obj = self.pool.get('ir.model.data')
        suppliers_location_id = model_data_obj.xmlid_to_res_id(
            cr, SUPERUSER_ID, 'stock.stock_location_suppliers')
        picking_type_in_id = stock_picking_type_obj.search(
            cr, uid, [('code', '=', 'incoming')], context=context)
        picking_type_out_id = stock_picking_type_obj.search(
            cr, uid, [('code', '=', 'outgoing')], context=context)
        picking_type_in_obj = stock_picking_type_obj.browse(
            cr, uid, picking_type_in_id, context=context)[0]  # warehouse_id
        picking_type_out_obj = stock_picking_type_obj.browse(
            cr, uid, picking_type_out_id, context=context)[0]  # warehouse_id
        for wizard_line in picking_wizard.picking_line:
            if wizard_line.product_id.type!='service':
                order_line = wizard_line.purchase_order_line_id
                if order_line.product_qty > 0 and order_line.product_qty - order_line.delivered_qty > 0:
                    move_line_in_data.update({"picking_type_id": picking_type_in_id[0], "product_uom_qty": wizard_line.picking_current,
                                              "product_id": order_line.product_id.id, "name": order_line.name, "location_dest_id": wizard_line.location_id.id or
                                                                                                                                   order.location_id.id or picking_type_in_obj.default_location_dest_id.id,
                                              'origin': order.name, "location_id": picking_type_in_obj.default_location_src_id.id, "picking_id": picking_id[1],
                                              "product_uom": order_line.product_id.uom_id.id, 'prod_spec': order_line.prod_spec,
                                              'invoice_state': order.invoice_method == 'picking' and '2binvoiced' or 'none',
                                              'purchase_line_id': order_line.id,
                                              'warehouse_id': order.picking_type_id.warehouse_id.id,
                                              'route_ids': order.picking_type_id.warehouse_id and [(6, 0, [x.id for x in order.picking_type_id.warehouse_id.route_ids])] or [],
                                              'company_id': order.company_id.id,
                                              'date': order.date_order, 'partner_id': order_line.order_id.partner_id.id,
                                              'date_expected': fields.date.date_to_datetime(self, cr, uid, order_line.date_planned, context), })

                    move_line = stock_move_obj.create(
                        cr, uid, move_line_in_data, context=context)
                    move_lines.append(move_line)
                elif order_line.product_qty < 0 and abs(order_line.product_qty) - order_line.delivered_qty > 0:
                    move_line_out_data.update({"picking_type_id": picking_type_out_id[0], "product_uom_qty": wizard_line.picking_current,
                                               "product_id": order_line.product_id.id, "name": order_line.name, "location_dest_id": suppliers_location_id,
                                               'origin': order.name, "location_id": order.location_id.id or picking_type_out_obj.default_location_src_id.id,
                                               "picking_id": picking_id[0],
                                               "product_uom": order_line.product_id.uom_id.id, 'prod_spec': order_line.prod_spec,
                                               'invoice_state': order.invoice_method == 'picking' and '2binvoiced' or 'none',
                                               'purchase_line_id': order_line.id,
                                               'warehouse_id': order.picking_type_id.warehouse_id.id,
                                               'route_ids': order.picking_type_id.warehouse_id and [(6, 0, [x.id for x in order.picking_type_id.warehouse_id.route_ids])] or [],
                                               'company_id': order.company_id.id,
                                               'date': order.date_order,
                                               'date_expected': fields.date.date_to_datetime(self, cr, uid, order_line.date_planned, context)})
                    move_line = stock_move_obj.create(
                        cr, uid, move_line_out_data, context=context)
                    move_lines.append(move_line)
                order_line.write({'delivered_qty': order_line.delivered_qty + wizard_line.picking_current})

            # 更改采购订单明细上的已入库数
            # delivered_qty = order_line.delivered_qty + wizard_line.picking_current
            # order_line.write({'delivered_qty': delivered_qty})
        # if not move_lines:
        #     raise osv.except_osv(u'错误', u'不需要出(入)库操作,请核对后再进行操作!\n 1.纯服务类采购单！\n2.采购订单行数量全为量为零')
        result = None
        if move_lines:
            result = stock_move_obj.write(cr, uid, move_lines, {'state': 'confirmed'}, context=context)
        # 更改入库单的状态--“标记为未处理”--"准备移动" (强制移动或者确认状态会破坏库存)
        # move_lines = stock_move_obj.action_confirm(cr, uid, move_lines)
        # if context.get('option') == 'done':
        #     return stock_move_obj.write(cr, uid, move_lines, {'state': 'draft'}, context=context)
        # elif context.get('option') == 'assigned':
        #     return stock_move_obj.write(cr, uid, move_lines, {'state': 'draft'}, context=context)
        return result

    def get_common_picking_val(self, cr, uid, purchase, context=None):
        res = {'state': 'confirmed',
               'partner_id': purchase.partner_id.id,
               'date': purchase.date_order,
               'origin': purchase.name,
               'is_card': purchase.is_card}
        if purchase.partner_id.multi_address_ids:
            address = purchase.partner_id.multi_address_ids[0]
            res.update({'consignee': address.name, 'street': address.street,
                        'zip': address.zip, 'mobile_number': address.phone,
                        'phone_number': address.phone_number, 'province_id': address.province_id.id,
                        'city_id': address.city_id.id, 'county_id': address.county_id.id})
        return res
    
    # 生成入库单，并且更改入库助手状态
    def picking(self, cr, uid, ids, context=None):
        purchase_order_obj = self.pool.get('purchase.order')
        purchase_order_ids = context.get('purchase_order_id')
        # 创建入库单
        purchase_orders = purchase_order_obj.browse(
            cr, uid, purchase_order_ids, context=context)
        stock_picking_type_obj = self.pool.get("stock.picking.type")
        picking_type_out_id = stock_picking_type_obj.search(cr, uid, [('code', '=', 'outgoing')], context=context)
        picking_type_in_id = stock_picking_type_obj.search(cr, uid, [('code', '=', 'incoming')], context=context)
        picking_type_out_obj = stock_picking_type_obj.browse(cr, uid, picking_type_out_id, context=context)[0]  # warehouse_id
        picking_type_in_obj = stock_picking_type_obj.browse(cr, uid, picking_type_in_id, context=context)[0]  # warehouse_id
        picking_out_id, picking_in_id = False, False
        for order in purchase_orders:
            picking_data = self.get_common_picking_val(cr, uid, order, context=context)
            if order.is_return or order.is_exchange:
                picking_data.update({'picking_type_id': picking_type_out_obj.id})
                picking_out_id = self.pool.get('stock.picking').create(
                    cr, uid, picking_data, context=context)
            if not order.is_return:
                picking_data.update({'picking_type_id': picking_type_in_obj.id})
                picking_in_id = self.pool.get('stock.picking').create(
                    cr, uid, picking_data, context=context)
            self.insert_stock_move(
                cr, uid, ids, [picking_out_id, picking_in_id], order, context=context)

            # 修改入库状态
            # if order.pick_state != True:
            #     order_line_ids = purchase_order_line_obj.search(
            #         cr, uid, ['&', ('order_id', '=', order.id), ('incoming_qty', '>', 0)], context=context)
            #     if not len(order_line_ids):
            #         purchase_order_obj.write(cr, uid, purchase_order_ids, {
            #                                  'is_picking_button_assigned': True}, context=context)
            #     else:
            #         purchase_order_obj.write(cr, uid, purchase_order_ids, {
            #                                  'is_picking_button_assigned': False}, context=context)
            if context.get('option') == 'done':
                if picking_out_id:
                    self.pool.get('stock.picking').action_assign(cr, uid, picking_out_id, context=context)
                if picking_in_id:
                    self.pool.get('stock.picking').action_assign(cr, uid, picking_in_id, context=context)
                if picking_out_id and \
                    self.pool.get('stock.picking').browse(cr, uid, picking_out_id, context=context).state == 'assigned':
                    self.pool.get('stock.picking').action_done(cr, uid, picking_out_id, context=context)
                if picking_in_id and \
                                self.pool.get('stock.picking').browse(cr, uid, picking_in_id,
                                                                      context=context).state == 'assigned':
                    self.pool.get('stock.picking').action_done(cr, uid, picking_in_id, context=context)
            elif context.get('option') == 'assigned':
                if picking_out_id:
                    self.pool.get('stock.picking').action_assign(cr, uid, picking_out_id, context=context)
                if picking_in_id:
                    self.pool.get('stock.picking').action_assign(cr, uid, picking_in_id, context=context)
            return [picking_out_id, picking_in_id]

    # "入库（移动）"按钮,校验入库数量是否未填而生成空入库单的问题
    def picking_button_done(self, cr, uid, ids, context=None):
        purchase_order_obj = self.pool.get('purchase.order')
        picking_wizard_obj = self.pool.get('purchase.picking.line')
        picking_wizard_ids = picking_wizard_obj.search(cr, uid, [(
            'picking_wizard', 'in', ids), ('picking_current', '!=', False)], context=context)

        context.update({'option': 'done'})
        purchase_order_ids = context.get('purchase_order_id')
        purchase_order_obj.write(cr, uid, purchase_order_ids, {
                                 'is_picking_button_assigned': True}, context=context)

        for wizard_obj in self.browse(cr,uid,ids,context=context):
            for line in wizard_obj.picking_line:
                if not line.location_id:
                    raise osv.except_osv(u'错误', u'必须填写产品行入（出）库的库位！\n请检查后再进行操作，如有疑问请联系管理员！')
        #生成出入库单
        stock_picking_ids = self.picking(cr, uid, ids, context=context)

        if picking_wizard_ids:
            # 调用系统入库按钮“移动”对应的方法do_enter_transfer_details，完成入库操作\
            for stock_picking_id in stock_picking_ids:
                self.pool.get("stock.picking").do_enter_transfer_details(
                    cr, uid, [stock_picking_id], context=context)
                transfer_details_ids = self.pool.get('stock.transfer_details').search(
                    cr, uid, [('picking_id', '=', stock_picking_id)], context=context)
                self.pool.get('stock.transfer_details').do_detailed_transfer(
                    cr, uid, transfer_details_ids, context=context)
        else:
            raise osv.except_osv(u'错误', u'没有填写任何“本次入库数量”，不能进行入库操作')

    # "入库（准备移动）"按钮，校验入库数量是否未填而生成空入库单的问题
    def picking_button_assigned(self, cr, uid, ids, context=None):
        purchase_order_obj = self.pool.get('purchase.order')
        purchase_picking_line_obj = self.pool.get('purchase.picking.line')
        purchase_picking_line_id = purchase_picking_line_obj.search(cr, uid, [(
            'picking_wizard', 'in', ids), ('picking_current', '!=', False)], context=context)

        if purchase_picking_line_id:
            context.update({'option': 'assigned'})
            purchase_order_ids = context.get('purchase_order_id')
            purchase_order_obj.write(cr, uid, purchase_order_ids, {
                                     'is_picking_button_assigned': True}, context=context)

            return self.picking(cr, uid, ids, context=context)
        else:
            raise osv.except_osv(u'错误', u'没有填写任何“本次入库数量”，不能进行入库操作')

    _columns = {
        'picking_line': fields.one2many('purchase.picking.line', 'picking_wizard', string=u'向导明细'),
    }


class purchase_picking_line(osv.osv_memory):
    # 入库助手向导明细
    _name = 'purchase.picking.line'
    _description = u'入库助手向导明细'

    def _compute_picking_available(self, cr, uid, ids, name, arg, context=None):
        """计算出本条记录可以出库的数量"""
        res = {}
        for picking_line in self.browse(cr, uid, ids, context=context):
            picking_available = picking_line.order_qty - picking_line.incomed_qty
            res.update({picking_line.id: picking_available})
        return res

    def on_change_picking_current(self, cr, uid, ids, order_qty, delivered_qty, picking_current, context=None):
        """防止 用户 胡乱输入的本次出(入库)的数量 本次发货数量不能大于未建发货单的数量"""
        if picking_current > abs(order_qty) - delivered_qty:
            return {'warning': {'title': u'错误', 'message': u"本次发货数量不能大于未建发货单的数量"},
                    "value": {"picking_current": abs(order_qty) - delivered_qty}, }

    def create(self, cr, uid, vals, context=None):
        if vals.get('order_qty') < 0:
            model_data_obj = self.pool.get('ir.model.data')
            stock_location_suppliers_id = model_data_obj.xmlid_to_res_id(
                cr, SUPERUSER_ID, 'stock.stock_location_suppliers')
            vals.update({'location_id': stock_location_suppliers_id})
        return super(purchase_picking_line, self).create(cr, uid, vals, context=context)

    _columns = {
        'picking_wizard': fields.many2one('purchase.picking.wizard', string=u'入库助手向导'),
        'product_id': fields.many2one('product.product', string=u"产品"),
        'partner_id': fields.many2one('res.partner', string=u"供应商"),
        'purchase_order_line_id': fields.many2one('purchase.order.line', string=u'采购订单明细ID'),
        'order_qty': fields.float(u'订单数量', digits_compute=dp.get_precision('Product Unit of Measure')),
        'incomed_qty': fields.float(u'已入库数', digits_compute=dp.get_precision('Product Unit of Measure')),
        'delivered_qty': fields.float(u'已建入库单数量', digits_compute=dp.get_precision('Product Unit of Measure')),
        'picking_available': fields.function(_compute_picking_available, string=u'可入库数'),
        'picking_current': fields.float(u'本次入库数量', digits_compute=dp.get_precision('Product Unit of Measure')),
        'location_id':fields.many2one('stock.location',u'目的库位'),
        'po_location_id':fields.many2one('stock.location',u'库位'), # #1050 采购助手的入库向导填写入库库位时只过滤出采购单的库位和下级库位 [Issue开启]
    }


class purchase_need(osv.osv):
    # 采购需求
    _name = 'purchase.need'
    _description = u'生成采购需求'

    def write(self,cr,uid,ids,vals,context=None):
        for need in self.browse(cr,uid,ids,context=context):
            if vals.get('choiced_so_ids','defalut_vals')!='defalut_vals' and \
                    need.choiced_so_ids and need.write_uid.id !=uid and uid !=SUPERUSER_ID:
                return {'message':'error','user_name':need.write_uid.name}
        return super(purchase_need, self).write(cr,uid,ids,vals,context=context)



    def name_get(self, cr, uid, ids, context=None):
        """ name_get方法 是为了 方便的识别此条记录所属的 purchase_need的记录"""
        res = []
        for contacts_ini in self.browse(cr, uid, ids, context=context):
            if contacts_ini.prod_spec:
                res.append((contacts_ini.id, "[%s]%s(%s)" % (contacts_ini.product_id.default_code, contacts_ini.product_id.name, contacts_ini.prod_spec)))
            else:
                res.append((contacts_ini.id, "[%s]%s" % (contacts_ini.product_id.default_code, contacts_ini.product_id.name)))
        return res

    # 产品对应的询价单剩余到货数，采购单剩余到货数
    def _compute_other_qty(self, cr, uid, ids, name, arg, context=None):
        res = {}
        for purchase_need_id in ids:
            purchase_need = self.browse(
                cr, uid, purchase_need_id, context=context)[0]
            # 获取采购订单明细中的预计到货数
            purchase_order_line_obj = self.pool.get('purchase.order.line')
            purchase_order_line_ids = purchase_order_line_obj.search(
                cr, uid, [('product_id', '=', purchase_need.product_id.id), ('prod_spec', '=', purchase_need.prod_spec)], context=context)
            purchase_order_line = purchase_order_line_obj.browse(
                cr, uid, purchase_order_line_ids, context=context)
            #inquiry_incoming_qty = 0
            purchase_incoming_qty = 0
            for line in purchase_order_line:
                #purchase_order = self.pool.get('purchase.order').browse(
                #    cr, uid, line.order_id.id, context=context)
                # if purchase_order.state in ('draft', 'sent', 'bid', 'confirmed'):
                #     inquiry_incoming_qty += line.incoming_qty
                if line.order_id.state != 'cancel':
                    purchase_incoming_qty += line.incoming_qty
            res.update({purchase_need_id: {
                #'inquiry_incoming_qty': inquiry_incoming_qty,
                'purchase_incoming_qty': purchase_incoming_qty,
            }})
        return res

    # 计算已经采购处理过的但是还未出库的产品数量
    def _compute_non_delivery_qty(self, cr, uid, ids, name, arg, context=None):
        res = {}
        # 原sql
        # sql = """SELECT pn.id,
        #                      pn.product_id,pn.prod_spec,
        #                      COALESCE(tmp1.non_delivery_qty,0) AS line_not_delivery_qty,
        #                      pn.state
        #                      FROM purchase_need AS pn
        #                      LEFT JOIN (SELECT product_id ,
        #                      SUM(line_not_delivery_qty) AS non_delivery_qty
        #                      FROM  (SELECT   product_id ,
        #                      (SELECT SUM(change_quantity)
        #                      FROM sale_order_line_change
        #                      WHERE po_id > 0 AND so_line_id = sale_order_line.id) - COALESCE(shipped_qty,0) AS line_not_delivery_qty
        #                      FROM sale_order_line) tmp2
        #                      WHERE line_not_delivery_qty > 0
        #                      GROUP BY product_id) AS tmp1
        #                      ON pn.product_id = tmp1.product_id"""
        # 振宇优化后sql
        # sql = """SELECT
        #             pn. id,
        #             pn.product_id,
        #             pn.prod_spec,
        #             (tmp1.no1 - tmp2.no2) AS line_not_delivery_qty,
        #             pn. state
        #         FROM
        #             purchase_need AS pn
        #         INNER join(
        #             SELECT
        #                 sochange.product_id,
        #                 SUM(sochange.change_quantity) AS no1
        #             FROM
        #                 sale_order_line_change sochange
        #             WHERE
        #                 sochange.po_id > 0
        #             GROUP BY
        #                 sochange.product_id
        #         ) tmp1 ON pn.product_id = tmp1.product_id
        #         INNER join(
        #             SELECT
        #                 sol.product_id,
        #                 sum(COALESCE(sol.shipped_qty, 0)) AS no2
        #             FROM
        #                 sale_order_line sol
        #             GROUP BY
        #                 sol.product_id
        #         ) tmp2 ON tmp1.product_id = tmp2.product_id
        #         WHERE pn.state='new'"""

        # 张旭继续优化
        sql = """SELECT
                    pn.id,
                    pn.product_id,
                    pn.prod_spec,
                    pn.stock_location_id,
                    (SELECT
                              sum((SELECT sum(solc.change_quantity)
                                      FROM sale_order_line_change solc
                                      WHERE solc.so_line_id=sol.id
                                        AND solc.po_id>0))
                           - sum(COALESCE(sol.shipped_qty,0))
                      FROM sale_order_line sol
                      WHERE sol.product_uom_qty > COALESCE(sol.shipped_qty, 0)
                           AND sol.product_id=pn.product_id AND sol.prod_spec IS NOT DISTINCT FROM pn.prod_spec ) AS line_not_delivery_qty
                FROM purchase_need AS pn
                WHERE pn.state='new'"""
        cr.execute(sql)
        query_data = cr.dictfetchall()
        for data in query_data:
            res.update({data['id']: data['line_not_delivery_qty']})
        return res

    # 计算采购需求量
    def _compute_purchase_need_qty(self, cr, uid, ids, name, arg, context=None):
        res = {}

        # 如果询价/采购单未入库数字为正数，减去数值；如果询价/采购单未入库数为负数，不做任何加减的操作
        for need in self.browse(cr, uid, ids, context=context):
            #purchase_need_qty = 0.0
            purchase_need_qty = need.sale_change_qty + need.non_delivery_qty \
                    - need.available_qty - need.purchase_incoming_qty - need.sale_return_not_in
            #可以不用考虑采购(询价单)入库数的正负情况,因为在取得采购(询价单)入库数的时候已经排除掉了关联销售订单的负数采购单
            # if need.inquiry_incoming_qty > 0 and need.purchase_incoming_qty <= 0:
            #     purchase_need_qty = need.sale_change_qty + need.non_delivery_qty - \
            #         need.available_qty - need.inquiry_incoming_qty
            # if need.inquiry_incoming_qty <= 0 and need.purchase_incoming_qty > 0:
            #     purchase_need_qty = need.sale_change_qty + need.non_delivery_qty - \
            #         need.available_qty - need.purchase_incoming_qty
            # if need.inquiry_incoming_qty > 0 and need.purchase_incoming_qty > 0:
            #     purchase_need_qty = need.sale_change_qty + need.non_delivery_qty - \
            #         need.available_qty - need.purchase_incoming_qty - need.inquiry_incoming_qty
            # if need.inquiry_incoming_qty <= 0 and need.purchase_incoming_qty <= 0:
            #     purchase_need_qty = need.sale_change_qty + need.non_delivery_qty - need.available_qty
            # 是为了应付采购手动下退货采购单的问题(这个问题已经跟采购说了,不能手动下退货单,要么把手动下的删除掉)
            # if need.sale_change_qty < 0:
            #     purchase_need_qty = need.sale_change_qty

            res.update(
                {need.id: purchase_need_qty if purchase_need_qty else 0})

        return res

    # 采购需求表根据so_ids将销售订单的名字拼接
    def _compute_so_code_list(self, cr, uid, ids, name, arg, context=None):
        res = {}
        for need in self.browse(cr, uid, ids, context=context):
            code_list = []
            so_list = eval(need.so_ids)
            if so_list:
                product_id = need.product_id.id
                sql = "SELECT so_id, change_quantity FROM sale_order_line_change solc" \
                      " INNER JOIN sale_order so ON solc.so_id=so.id WHERE so_id in (%s) AND product_id=%s and po_id is null" % (need.so_ids[1:-1], product_id)
                cr.execute(sql)
                sale_order_line_rows = cr.fetchall()
                product_qty = 0
                order_id_qty_map = {}
                for sale_order_line_row in sale_order_line_rows:
                    order_id = sale_order_line_row[0]
                    product_qty = int(sale_order_line_row[1])
                    order_id_qty_map.update({order_id: product_qty+order_id_qty_map.get(order_id,0)})

                choiced_so_ids = []
                if need.choiced_so_ids:
                    choiced_so_ids = eval(need.choiced_so_ids)
                for so in self.pool.get("sale.order").browse(cr, uid, so_list, context=context):
                    change_qty = order_id_qty_map.get(so.id)
                    change_text = u'加'
                    if change_qty < 0:
                        change_text = '减'
                    if so.expected_delivery_time:
                        simple_date = so.expected_delivery_time[5:7] + so.expected_delivery_time[8:10]
                    checked = False
                    try:
                        if choiced_so_ids.index(so.id) >= 0:
                            checked = True
                    except ValueError:
                        pass
                    if change_qty is None:
                        change_qty = 0
                    code_list.append({'so_id': so.id, 'so_code': so.name, 'change_qty': change_qty, 'date': simple_date, 'change_text': change_text + str(abs(change_qty)), 'checked': checked})
            else:
                code_list = ' '
            res.update({need.id: simplejson.dumps(code_list)})
        return res

    def _compute_last_date_purchase(self, cr, uid, ids, name, arg, context=None):
        """计算最晚下单日期"""
        res = {}
        for need in self.browse(cr, uid, ids, context=context):
            if need.expected_delivery_time:
                res.update({need.id: datetime.strptime(need.expected_delivery_time, "%Y-%m-%d").date() - timedelta(days=need.arrival_delay)})
        return res

    def _compute_available_qty(self, cr, uid, ids, name, arg, context=None):
        res = {}
        need_product_ids = [need.product_id.id for need in self.browse(cr, uid, ids, context=context)]
        result_dict = self.pool.get(
            'product.product').get_product_location_available_qty(cr, uid, need_product_ids, context=context)
        for need in self.browse(cr, uid, ids, context=context):
            location_ids=self.pool.get('stock.location').get_child_location_ids(cr,uid,need.stock_location_id.id,context=context)
            res.update(dict([(need.id, sum([result_dict.get((need.product_id.id, need.prod_spec or None, location_id),0)
                                                 for location_id in location_ids]))]))
        return res
    _columns = {
        'product_code': fields.related('product_id', 'default_code', type='char', string='产品编号'),
        'product_id': fields.many2one('product.product', string=u'产品'),
        'supplier_id': fields.many2one('res.partner', string=u'供应商'),
        'prod_spec': fields.char(string=u'型号备注'),
        'change_ids': fields.char(string=u'变更表ID'),
        'so_ids': fields.char(string=u'销售订单ID'),
        'choiced_so_ids': fields.char(string=u'选择处理的销售订单'),
        'so_code_list': fields.function(_compute_so_code_list, string=u'销售订单列表', type='text', store=False),
        'so_line_ids': fields.char(string=u'销售订单行ids'),
        'expected_delivery_time': fields.date(string=u"最早预计发货时间"),
        'state': fields.selection([
            ('new', '新建'),
            ('done', '已处理'),
        ], u'状态', readonly=True),
        #'available_qty': fields.function(_compute_available_qty, type='float', string=u'已有库存数', store=True, readonly='1'),
        'available_qty': fields.float(string=u'可用库存', digits_compute=dp.get_precision('Product Unit of Measure')),
        'sale_change_qty': fields.float(string=u'订单变更', digits_compute=dp.get_precision('Product Unit of Measure')),
        'non_delivery_qty': fields.float(string=u'已采未出', digits_compute=dp.get_precision('Product Unit of Measure')),
        #'non_delivery_qty': fields.function(_compute_non_delivery_qty, type='float', digits_compute=dp.get_precision('Product Unit of Measure'), string=u'未出库量',
        #                                    store=True),
        #'inquiry_incoming_qty': fields.float(string=u'询未到', digits_compute=dp.get_precision('Product Unit of Measure')),
        #'inquiry_incoming_qty': fields.function(_compute_other_qty, type='float', multi=True, digits_compute=dp.get_precision('Product Unit of Measure'), string=u'已下询价单还未到货数', store=True),
        'sale_return_not_in': fields.float(string=u'销售退货', digits_compute=dp.get_precision('Product Unit of Measure')),
        'purchase_incoming_qty': fields.float(string=u'采未到', digits_compute=dp.get_precision('Product Unit of Measure')),
        #'purchase_incoming_qty': fields.function(_compute_other_qty, type='float', multi=True, digits_compute=dp.get_precision('Product Unit of Measure'), string=u'已下采购单还未到货数', store=True),
        'purchase_need_qty': fields.function(_compute_purchase_need_qty, type='float', digits_compute=dp.get_precision('Product Unit of Measure'), string=u'需求量', store={
            _name: (lambda self, cr, uid, ids, context=None: ids, ['available_qty', 'sale_change_qty', 'non_delivery_qty', 'sale_return_not_in', 'purchase_incoming_qty'], 100), }),
        'arrival_delay': fields.integer(u"到货周期"),
        'last_date_purchase': fields.function(_compute_last_date_purchase, type="date", string=u'最晚下单日期', store={
            'purchase.need': (lambda self, cr, uid, ids, c={}: ids, ['arrival_delay', "expected_delivery_time"], 10),
        }),
        'priority': fields.integer(u"优先级"),
        'is_check_result': fields.boolean(u'是否检查结果'),
        'stock_location_id': fields.many2one('stock.location', u'库位'),
        'supplier_list': fields.char(u'供应商列表'),
    }
    #_sql_constraints = [('uniq_purchase_need', 'unique (change_ids, is_check_result)', u'变更记录不能重复生成采购需求')]
    _defaults = {
        'arrival_delay': 30,
        'is_check_result': False,
    }
    #存储过程
    def init(self, cr):
        cr.execute("""DROP VIEW IF EXISTS new_purchase_needs;
CREATE or REPLACE VIEW new_purchase_needs  AS (
SELECT new_needs.*, exists_needs.need_id FROM (
    SELECT pp.id AS product_id,
          solc.prod_spec AS prod_spec,
          '{'||array_to_string(array_agg(solc.id ORDER BY solc.id), ',')||'}' AS change_ids,
          solc.stock_location_id as stock_location_id,
          array_agg(DISTINCT solc.so_id) AS so_ids,
          min(so.expected_delivery_time) AS expected_delivery_time,
          (SELECT array_agg(rp.supplier_code) FROM product_supplierinfo ps
               INNER JOIN res_partner rp ON rp.id=ps.name WHERE ps.product_tmpl_id=pp.product_tmpl_id) AS supplier_list,
          'new' AS state,
          sum(solc.change_quantity) AS sale_change_qty,

          (
             /*(SELECT
                -- 取得销售订单行已采购数减去已出库数(有可能为空,再包一层COALESCE)
                COALESCE(
                -- 取得销售订单行已采购数
                sum((SELECT COALESCE(sum(solc2.change_quantity), 0)
                     FROM sale_order_line_change solc2
                     WHERE solc2.so_line_id=sol.id AND solc2.po_id>0
                            AND solc2.stock_location_id=solc.stock_location_id))
                -- 减去已出库数
                - COALESCE(sum(sol.shipped_qty),0), 0)

               FROM sale_order_line sol
               INNER JOIN sale_order so ON so.id=sol.order_id
               INNER JOIN order_type ot ON so.order_type_id = ot.id
               WHERE ((sol.product_uom_qty > COALESCE(sol.shipped_qty, 0)) OR
                        sol.id IN (SELECT so_line_id FROM sale_order_line_change WHERE change_quantity<0 AND product_id=sol.product_id AND po_id IS NULL))
                     AND sol.product_id=pp.id
                     AND ot.stock_location_id = solc.stock_location_id
                     AND UPPER(COALESCE(sol.prod_spec,''))=UPPER(COALESCE(solc.prod_spec,''))
                     -- 取消的订单不能算未出库数!？(取消的但是采购过的要算！
                     -- AND so.state<>'cancel'
               )

                -- 减去已采购的已保留数
              - (SELECT
                 CAST(COALESCE((SELECT sum(sq.qty) FROM stock_move sm
                    INNER JOIN stock_picking sp ON sp.id=sm.picking_id
                    INNER JOIN stock_quant sq ON sq.reservation_id=sm.id
                    WHERE sm.product_id=pp.id
                          AND sp.so_id = ANY(array_agg(sol.order_id))
                   ),0) AS numeric)
               FROM sale_order_line sol
                   INNER JOIN sale_order_line_change solc2 ON solc2.so_line_id=sol.id
               WHERE sol.product_uom_qty > COALESCE(sol.shipped_qty, 0)
                   AND solc2.po_id > 0
                   AND sol.product_id=pp.id
                   AND solc2.stock_location_id=solc.stock_location_id
                   AND UPPER(COALESCE(sol.prod_spec,''))=UPPER(COALESCE(solc.prod_spec,''))
               )*/

               -- 已处理未发货完成的变更数(或者有负变更数的销售订单行的已处理的正变更数) 减去 已发货数 减去 已采购的库存保留数(代替上方一大段2017.09.11优化)
               COALESCE((SELECT sum(solc2.change_quantity)
                  --减去已发货数
                  - COALESCE((SELECT sum(COALESCE(shipped_qty,0)) FROM sale_order_line WHERE id =any(array_agg(sol.id))),0)
                  -- 减去已采购的库存保留数
                  - COALESCE((SELECT sum(sq.qty) FROM stock_move sm
                    INNER JOIN stock_picking sp ON sp.id=sm.picking_id
                    INNER JOIN stock_quant sq ON sq.reservation_id=sm.id
                    WHERE sm.product_id=pp.id
                          AND sp.so_id = ANY(array_agg(sol.order_id))
                   ),0)
                 FROM sale_order_line sol
                 INNER JOIN sale_order_line_change solc2 ON solc2.so_line_id=sol.id
                 WHERE (sol.product_uom_qty>COALESCE(sol.shipped_qty, 0) OR sol.id IN (SELECT so_line_id FROM sale_order_line_change WHERE po_id IS NULL AND change_quantity<0))
                 AND solc2.po_id >0 AND solc2.product_id=sol.product_id
                 AND solc2.stock_location_id=solc.stock_location_id AND sol.product_id=pp.id AND UPPER(COALESCE(sol.prod_spec,''))=UPPER(COALESCE(solc.prod_spec,''))
              ),0)


                -- 需要加上被删除的订单行的变更记录未出库数(订单行被删除无法出库，但是也要算未出库数)
              + (SELECT COALESCE(sum(solc2.change_quantity),0) FROM sale_order_line_change solc2
--                    LEFT JOIN sale_order_line sol ON sol.id=solc2.so_line_id
                    WHERE solc2.so_line_id IS NULL AND solc2.po_id > 0
                        AND solc2.product_id=pp.id
                        AND solc2.stock_location_id=solc.stock_location_id
                        AND UPPER(COALESCE(solc2.prod_spec,''))=UPPER(COALESCE(solc.prod_spec,''))
                        AND solc2.so_id = ANY(array_agg( solc.so_id))
                 )


          ) AS non_delivery_qty, -- 含义改为未出库且未保留数

          COALESCE((SELECT sum(qty)
                    FROM stock_quant WHERE product_id=pp.id
                      AND (location_id=solc.stock_location_id OR location_id IN (SELECT id FROM stock_location WHERE location_id=solc.stock_location_id))
                       AND UPPER(COALESCE(prod_spec,''))=UPPER(COALESCE(solc.prod_spec,''))
-- 已保留的不能算库存(销售订单行的已出库数的含义已改为未保留数)
                      AND (reservation_id is NULL OR reservation_id=-1 AND so_id is NOT NULL)
                    GROUP BY product_id), 0)
          AS available_qty, -- 含义改为在手可用(未保留数)库存
-- 目前2种退货询价单(关联销售订单和未关联销售订单)都算在未入库数里了, 要区分库位
          --COALESCE((SELECT SUM(CASE WHEN incoming_qty<>0 OR po.so_ids IS NULL OR po.so_ids='[]' THEN pol.incoming_qty ELSE 0 END)
          --      FROM purchase_order_line pol INNER JOIN purchase_order po ON po.id=pol.order_id
          --       WHERE pol.product_id=pp.id AND UPPER(COALESCE(pol.prod_spec, ''))=UPPER(COALESCE(solc.prod_spec,''))
          --          AND po.location_id = solc.stock_location_id
          --          AND pol.incoming_qty<>0 AND pol.state IN ('draft')), 0)
          --AS inquiry_incoming_qty,
-- 增加销售退货未入库数
          (SELECT ABS(SUM(product_uom_qty + COALESCE(sol.shipped_qty, 0))) FROM sale_order_line sol WHERE sol.product_id=pp.id
            AND sol.product_uom_qty<0 AND sol.state NOT IN ('cancel', 'draft') AND abs(sol.product_uom_qty)>COALESCE(sol.shipped_qty, 0)
            ) AS sale_return_not_in,

-- 目前2种退货采购单都算(关联销售订单和未关联销售订单)在未入库数里了, 要区分库位
          COALESCE((SELECT SUM(pol.incoming_qty)
                -- 扣除已保留的采购退单数
                + COALESCE((SELECT sum(sq.qty) FROM stock_move sm INNER JOIN stock_picking sp ON sp.id=sm.picking_id
                 INNER JOIN stock_quant sq ON sq.reservation_id=sm.id WHERE sm.purchase_line_id=any(array_agg(pol.id)) AND sm.product_id=pp.id
                  and (sm.location_id=solc.stock_location_id or sm.location_id IN (SELECT id FROM stock_location WHERE location_id=solc.stock_location_id) )), 0)
            FROM purchase_order_line pol INNER JOIN purchase_order po ON po.id=pol.order_id
              WHERE pol.product_id=pp.id AND UPPER(COALESCE(pol.prod_spec, ''))=UPPER(COALESCE(solc.prod_spec,''))
                  AND (po.location_id = solc.stock_location_id OR po.location_id IN (SELECT id FROM stock_location WHERE location_id=solc.stock_location_id))
                  AND pol.incoming_qty<>0 AND pol.state NOT IN ('cancel')), 0)
          AS purchase_incoming_qty,

          NULL AS purchase_need_qty,
          FALSE AS is_check_result
          FROM    sale_order AS so
          RIGHT JOIN  sale_order_line_change AS solc ON so.id=solc.so_id
          INNER JOIN  product_product AS  pp  ON solc.product_id=pp.id
          LEFT JOIN   product_supplierinfo  AS ps ON pp.product_tmpl_id=ps.product_tmpl_id
          WHERE solc.po_id IS NULL
          GROUP BY pp.id, pp.product_tmpl_id, solc.prod_spec, solc.stock_location_id ORDER BY pp.id
) AS new_needs
LEFT JOIN (
    SELECT id AS need_id, stock_location_id, product_id, prod_spec, sale_change_qty, '{' || substring(change_ids, 2, char_length(change_ids)-2) || '}' AS change_ids
        FROM purchase_need WHERE      state='new' AND (is_check_result=FALSE OR is_check_result IS NULL)
) AS exists_needs
    ON new_needs.product_id = exists_needs.product_id
        AND UPPER(COALESCE(new_needs.prod_spec,'')) = UPPER(COALESCE(exists_needs.prod_spec,''))
        AND new_needs.change_ids = exists_needs.change_ids
        AND new_needs.sale_change_qty = exists_needs.sale_change_qty
        AND new_needs.stock_location_id = exists_needs.stock_location_id
WHERE need_id IS NULL)""")


class purchase_need_refresh_wizard(osv.osv_memory):
    _name = 'purchase.need.refresh.wizard'
    _description = u'采购需求界面，点击刷新按钮后弹出的界面'

    def confirm_button(self, cr, uid, ids, context=None):
        #增加线程锁防止多人同时执行(也没用...大哭)
        #lock = RegistryManager.lock()
        #lock.acquire()
        # 刷新采购需求一开始就锁定需求表(锁需求表没用, 换向导表, 也没用...哭死)
        cr.execute("LOCK TABLE purchase_need_refresh_wizard IN ACCESS EXCLUSIVE MODE")
        # 再检查是否有人在刷新的标记
        cr.execute("SELECT ru.login FROM purchase_need_refresh_wizard pnrw INNER JOIN res_users ru ON ru.id=pnrw.write_uid WHERE is_refreshing=TRUE")
        refresh_need_rows = cr.dictfetchall()
        if refresh_need_rows:
            #lock.release()
            raise osv.except_osv(
                    u'错误', u'"%s"正在刷新，请稍等' % (refresh_need_rows[0].get('login')))
        #else:
        #    cr.execute("UPDATE purchase_need_refresh_wizard SET is_refreshing=TRUE WHERE id=%s" % (ids[0]))
        #    cr.commit()
        for need_wizard in self.browse(cr, uid, ids, context=context):
            # latest_purchase_date = need_wizard.latest_purchase_date
            # 如果是检查采购结果
            if not need_wizard.is_requirement_card:
                self.sql_get_purchase_need_result(cr, uid, need_wizard.check_result, need_wizard.is_relating_supplier
                                              , context=context)
            else:
                self.sql_card_prodcut_purchase_need_result(cr, uid, need_wizard.check_result, need_wizard.is_relating_supplier
                                                  , context=context)
        #cr.execute("UPDATE purchase_need_refresh_wizard SET is_refreshing=FALSE WHERE id=%s" % (ids[0]))
        #lock.release()

    def change_data_constraction(self, cr, uid, dict_list, context=None):
        res = {}
        for dict_row in dict_list:
            res.update({dict_row.get('product_id'): dict_row})
        return res

    def get_exchange_order_location(self, cr, uid, context=None):
        model_data_obj = self.pool.get('ir.model.data')
        card_exchange_order_id = model_data_obj.xmlid_to_res_id(
            cr, SUPERUSER_ID, 'dftg_ext.dftg_order_type_data_card_exchange_order')
        order_type_exchange = self.pool.get('order.type').browse(cr, uid, card_exchange_order_id, context=context)
        card_location_id = order_type_exchange.stock_location_id.id
        child_card_location_ids = self.pool.get('stock.location') \
            .get_child_location_ids(cr, uid, card_location_id, context=context)
        return child_card_location_ids

    def refresh_available_stock_qty(self, cr, uid, context=None):
        # 刷新可用库存
        child_card_location_ids = self.get_exchange_order_location(cr, uid, context=context)
        cr.execute("SELECT product_id, sum(qty) as available_qty FROM stock_quant"
                   " WHERE reservation_id IS NULL AND location_id IN %s"
                   " GROUP BY product_id", (tuple(child_card_location_ids),))
        product_quant_rows = cr.dictfetchall()
        return self.change_data_constraction(cr, uid, product_quant_rows, context=context)

    def get_no_locked_no_delivery(self, cr, uid, context=None):
        cr.execute("""SELECT sol.product_id,
        sum(sol.product_uom_qty) AS non_delivery_qty, ot.stock_location_id AS stock_location_id
        FROM apply_delivery ad
        INNER JOIN sale_order so ON ad.so_id=so.id
        INNER JOIN order_type ot ON so.order_type_id=ot.id
        INNER JOIN sale_order_line sol ON sol.order_id=so.id
        WHERE ad.is_shipping = FALSE AND ad.is_canceled=FALSE AND so.is_stock_reserved=FALSE
        GROUP BY sol.product_id, ot.stock_location_id""")
        rows = cr.dictfetchall()
        return self.change_data_constraction(cr, uid, rows, context=context)

    #刷新采购未入库数
    def refresh_purchase_incoming_qty(self, cr, uid, context=None):
        child_card_location_ids = self.get_exchange_order_location(cr, uid, context=context)
        cr.execute("""SELECT pol.product_id as product_id, sum(
                          CASE WHEN pol.product_qty > 0 THEN
                            pol.product_qty - pol.incomed_qty
                          ELSE
                            pol.product_qty + pol.incomed_qty
                          END) AS purchase_incoming_qty
                        FROM purchase_order po
                        INNER JOIN purchase_order_line pol ON pol.order_id=po.id
                        WHERE po.location_id IN %s AND abs(pol.product_qty) > pol.incomed_qty AND po.state<>'cancel'
                        GROUP BY pol.product_id
        """, (tuple(child_card_location_ids), )) #tuple(product_ids)
        purchase_incoming_rows = cr.dictfetchall()
        return self.change_data_constraction(cr, uid, purchase_incoming_rows, context=context)

    def sql_card_prodcut_purchase_need_result(self, cr, uid, check_result, is_relating_supplier, context=None):
        need_obj = self.pool.get('purchase.need')
        model_data_obj = self.pool.get('ir.model.data')
        card_exchange_order_id = model_data_obj.xmlid_to_res_id(
            cr, SUPERUSER_ID, 'dftg_ext.dftg_order_type_data_card_exchange_order')
        order_type_exchange = self.pool.get('order.type').browse(cr, uid, card_exchange_order_id, context=context)
        need_ids = need_obj.search(cr, uid, [('stock_location_id', '=', order_type_exchange.stock_location_id.id),
                                             ('state', '=', 'new')], context=context)
        need_obj.unlink(cr, uid, need_ids, context=context)
        available_qty_dict = self.refresh_available_stock_qty(cr, uid, context=context)
        incoming_qty_dict = self.refresh_purchase_incoming_qty(cr, uid, context=context)
        no_locked_delivery_dict = self.get_no_locked_no_delivery(cr, uid, context=context)
        product_ids = list((set(available_qty_dict.keys()).union(set(incoming_qty_dict.keys()))
                       ).union(set(no_locked_delivery_dict.keys())))
        for product_id in product_ids:
            vals = available_qty_dict.get(product_id, {})
            vals.update(incoming_qty_dict.get(product_id, {}))
            vals.update(no_locked_delivery_dict.get(product_id, {}))
            vals.update({'expected_delivery_time':datetime.now(), 'so_ids': '[]', 'state': 'new', 'change_ids': '[]'})
            need_obj.create(cr, uid, vals, context=context)
        return True
    
    @dftg_decorate.redis_lock(error_str="采购助手刷新锁未解除，请等几秒再试")
    def sql_get_purchase_need_result(self, cr, uid, check_result, is_relating_supplier,
                                     context=None):
        try:
            res_partner_obj = self.pool.get("res.partner")
            # 查找是否创建“未指定供应商”
            search_supplier = res_partner_obj.search(
                cr, uid, [('name', '=', u'未指定供应商')], context=context)
            if search_supplier:
                undefine_supplier = search_supplier[0]
            else:
                undefine_supplier = res_partner_obj.create(
                    cr, uid, {'name': u'未指定供应商', 'supplier': True, 'customer': False}, context=context)
                # SQL语句联合查询
            purchse_need_obj = self.pool.get("purchase.need")
            if check_result:
                cr.execute("DELETE FROM purchase_need WHERE state='new' AND is_check_result=True")
                if is_relating_supplier:
                    cr.execute("""SELECT pp.id AS product_id,
                          pol.prod_spec AS prod_spec,
                          COALESCE(ps.name,%d) AS supplier_id,
                          ps.sequence AS priority,
                          COALESCE(ps.delay,30) As arrival_delay,
                          '{}' AS change_ids,
                          '{}' AS so_ids,
                          min(pol.date_planned) AS expected_delivery_time,
                          'new' AS state,
                          0 AS sale_change_qty,
                          NULL AS available_qty,
                          -- NULL AS inquiry_incoming_qty,
                          NULL AS sale_return_not_in,
                          NULL AS purchase_incoming_qty,
                          NULL AS purchase_need_qty,
                          True AS is_check_result
                          FROM  purchase_order_line pol
                          INNER JOIN  product_product AS  pp  ON pol.product_id=pp.id
                          LEFT JOIN   product_supplierinfo  AS ps ON pp.product_tmpl_id=ps.product_tmpl_id
                            INNER JOIN  purchase_order AS  po  ON pol.order_id=po.id
                              WHERE pol.product_uom - pol.incomed_qty > 0 and po.state<>'cancel'
                          GROUP BY pp.id, ps.name,ps.sequence,ps.delay,pol.prod_spec
                        """ % (undefine_supplier))
                else:
                    cr.execute("""SELECT pp.id AS product_id,
                                  solc.prod_spec as prod_spec,
                                  solc.stock_location_id as stock_location_id,
                                  '{}' as change_ids,
                                  array_agg(DISTINCT solc.so_id) as so_ids,
                                  array_agg(solc.so_line_id) as so_line_ids,
                                  min(so.expected_delivery_time) AS expected_delivery_time,
                                  'new' AS state,
                                  0 AS sale_change_qty,
                                  NULL AS available_qty,
                                  -- NULL AS inquiry_incoming_qty,
                                  NULL AS sale_return_not_in,
                                  NULL AS purchase_incoming_qty,
                                  NULL AS purchase_need_qty,
                                  True AS is_check_result
                                  FROM    sale_order AS so
                                  RIGHT JOIN  sale_order_line_change AS solc ON so.id=solc.so_id
                                  INNER JOIN  product_product AS  pp  ON solc.product_id=pp.id
                                  WHERE solc.po_id IS NOT NULL and solc.create_date > now()- interval'3 days'
                                  GROUP BY pp.id,solc.prod_spec,solc.stock_location_id
                        """)
            # 如果是刷新采购需求(从销售变更表中获取)
            else:
                # 删除需求表中库存、未入库数有变化的记录(已修正，但是未最终优化，应该根据算出来的需求量有无变化删除)
                cr.execute("""DELETE FROM purchase_need pn WHERE pn.state='new'
    AND pn.is_check_result=FALSE
    AND (
    pn.available_qty <> COALESCE((SELECT sum(qty)
        FROM stock_quant WHERE product_id=pn.product_id
            AND (location_id=pn.stock_location_id
                OR location_id IN (SELECT id FROM stock_location WHERE location_id=pn.stock_location_id))
            AND UPPER(COALESCE(prod_spec,''))=UPPER(COALESCE(pn.prod_spec,''))
    -- 已保留的不能算库存(销售订单行的已出库数的含义已改为未保留数)
            AND (reservation_id IS NULL OR reservation_id=-1 AND stock_quant.so_id is NOT NULL)
            GROUP BY product_id), 0)
    -- 目前2种退货询价单(关联销售订单和未关联销售订单)都算在未入库数里了, 要区分库位
    -- OR pn.inquiry_incoming_qty <> COALESCE((
    --            SELECT SUM(CASE WHEN incoming_qty <> 0 OR po.so_ids IS NULL OR po.so_ids='[]' THEN pol.incoming_qty ELSE 0 END)
    --              FROM purchase_order_line pol INNER JOIN purchase_order po ON po.id=pol.order_id
    --              WHERE pol.product_id=pn.product_id
    --                AND UPPER(COALESCE(pol.prod_spec, ''))=UPPER(COALESCE(pn.prod_spec,''))
    --                AND po.location_id = pn.stock_location_id
    --                AND pol.incoming_qty<>0 AND pol.state IN ('draft')), 0)
    -- 比较销售退货未入库数
    OR pn.sale_return_not_in <> (SELECT ABS(SUM(product_uom_qty + COALESCE(sol.shipped_qty, 0))) FROM sale_order_line sol
              WHERE sol.product_id=pn.product_id
                AND sol.product_uom_qty<0 AND sol.state NOT IN ('cancel', 'draft') AND abs(sol.product_uom_qty)>COALESCE(sol.shipped_qty, 0)
                )
    -- 目前2种退货采购单(询价单)都算(关联销售订单和未关联销售订单)在未入库数里了, 要区分库位
    OR pn.purchase_incoming_qty <> COALESCE((
                SELECT SUM(pol.incoming_qty)
                    -- 扣除已保留的采购退单数
                    - COALESCE((SELECT sum(sq.qty) FROM stock_move sm INNER JOIN stock_picking sp ON sp.id=sm.picking_id
                      INNER JOIN stock_quant sq ON sq.reservation_id=sm.id
                       WHERE sm.purchase_line_id=any(array_agg(pol.id)) and sm.product_id=pn.product_id and (sm.location_dest_id =pn.stock_location_id
                          OR sm.location_dest_id IN (SELECT id FROM stock_location WHERE location_id=pn.stock_location_id))
                       ), 0)
                  FROM purchase_order_line pol INNER JOIN purchase_order po ON po.id=pol.order_id
                  WHERE pol.product_id=pn.product_id
                    AND UPPER(COALESCE(pol.prod_spec, ''))=UPPER(COALESCE(pn.prod_spec,''))
                    AND (po.location_id = pn.stock_location_id OR po.location_id IN (SELECT id FROM stock_location WHERE location_id=pn.stock_location_id))
                    AND pol.incoming_qty<>0 AND pol.state NOT IN ('cancel')), 0))""")

                #需要先删除对应的库位的销售变更记录已经不存在的采购需求
                cr.execute("""DELETE FROM purchase_need pn WHERE pn.state='new'
          AND pn.product_id NOT IN (SELECT product_id FROM sale_order_line_change solc
                    WHERE solc.po_id IS null AND solc.stock_location_id=pn.stock_location_id OR solc.stock_location_id IN (SELECT id FROM stock_location WHERE location_id=pn.stock_location_id))
          AND pn.is_check_result=False""")

                # 删除数量或者对应的销售变更表ids有变化的需求表产品
                cr.execute("""DELETE FROM purchase_need WHERE id IN (
    SELECT need_id FROM (
      SELECT id AS need_id, stock_location_id,
        product_id, sale_change_qty, '{' || substring(change_ids, 2, char_length(change_ids)-2) || '}' AS     change_ids
      FROM purchase_need WHERE state='new' AND (is_check_result=FALSE OR is_check_result IS NULL)
    ) exists_needs
    LEFT JOIN (
        SELECT pp.id AS product_id,
                                  '{'||array_to_string(array_agg(solc.id ORDER BY solc.id), ',')||'}' AS change_ids,
                                  solc.stock_location_id AS stock_location_id,
                                  sum(solc.change_quantity) AS sale_change_qty
                                  FROM    sale_order AS so
                                  RIGHT JOIN  sale_order_line_change AS solc ON so.id=solc.so_id
                                  INNER JOIN  product_product AS  pp  ON solc.product_id=pp.id
                                  LEFT JOIN   product_supplierinfo  AS ps ON pp.product_tmpl_id=ps.product_tmpl_id
                                  WHERE solc.po_id IS NULL
                                  GROUP BY pp.id,solc.prod_spec,solc.stock_location_id ORDER BY pp.id
    ) AS new_needs
    
    ON new_needs.product_id = exists_needs.product_id
      AND new_needs.change_ids = exists_needs.change_ids
      AND new_needs.stock_location_id = exists_needs.stock_location_id
       WHERE new_needs.sale_change_qty <> exists_needs.sale_change_qty OR new_needs.product_id IS NULL
    )
    AND state='new'
    AND (is_check_result=FALSE OR is_check_result IS NULL)
                           """)

                # 关联供应商这种刷新方式暂时不用了
                if is_relating_supplier:
                    cr.execute(""" SELECT pp.id AS product_id,
                                  COALESCE(ps.name,%d) AS supplier_id,
                                  ps.sequence AS priority,
                                  COALESCE(ps.delay,30) As arrival_delay,
                                  solc.prod_spec as prod_spec,
                                  array_agg(solc.id) as change_ids,
                                  array_agg(DISTINCT solc.so_id) as so_ids,
                                  min(so.expected_delivery_time) AS expected_delivery_time,
                                  'new' AS state,
                                  sum(solc.change_quantity) AS sale_change_qty,
                                  NULL AS available_qty,
                                  -- NULL AS inquiry_incoming_qty,
                                  NULL AS sale_return_not_in,
                                  NULL AS purchase_incoming_qty,
                                  NULL AS purchase_need_qty,
                                  False as is_check_result
                                  FROM    sale_order AS so
                                  RIGHT JOIN  sale_order_line_change AS solc ON so.id=solc.so_id
                                  INNER JOIN  product_product AS  pp  ON solc.product_id=pp.id
                                  LEFT JOIN   product_supplierinfo  AS ps ON pp.product_tmpl_id=ps.product_tmpl_id
                                  WHERE solc.po_id IS NULL
                                  GROUP BY pp.id,ps.name,ps.sequence,ps.delay,solc.prod_spec""" % (undefine_supplier))
                else:
                    # 最常用的刷新采购需求，改成增量刷新(change_ids或者产品数量有变化的)
                    cr.execute("SELECT * FROM new_purchase_needs")

            purchase_need_data = cr.dictfetchall()
            for need in purchase_need_data:
                #删除不需要的字段
                del need['need_id']
                purchse_need_obj.create(cr, uid, need, context=context)
            cr.commit()
        except Exception as exc:
            raise osv.except_osv(u'错误！请稍后重试！', u'%s' % (exc))


    _columns = {
        'latest_purchase_date': fields.date(u"最晚采购时间", required=True),
        'is_relating_supplier': fields.boolean(u'是否关联供应商'),
        'is_requirement_card': fields.boolean(u'是否卡券需求'),
        'check_result': fields.boolean(u'检查采购单结果(3天内已采购的产品)'),
        'is_refreshing': fields.boolean(u'是否正在刷新')
    }

    # 默认查找时间是today+7days
    _defaults = {
        'latest_purchase_date': lambda *a: date.today() + timedelta(days=7),
        'is_requirement_card': lambda s, c, u, ctx: ctx.get('is_requirement_card'),
    }


class generate_purchase_order_wizard(osv.osv_memory):
    _name = 'generate.purchase.order.wizard'
    _description = u'询价单生成向导'

    def fields_view_get(self, cr, uid, view_id=None, view_type='form',
                        context=None, toolbar=False, submenu=False):
        """ 生成询价单向导 界面出来前的 一些条件的判断
        1.不能 选择了同个产品不能重复选择多条供应商记录
        2.  一次只能处理一个库位的采购需求"""
        if context is None:
            context = {}
        res = super(generate_purchase_order_wizard, self).fields_view_get(
            cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=False)
        if context.get('active_model', '') == 'purchase.need':

            active_ids = context.get('active_ids')
            # 检验所选采购需求中的产品是否有重复
            if len(active_ids) > 1:
                operation = 'in %s' % (str(tuple(active_ids)))
            else:
                operation = '= %d' % (active_ids[0])
            cr.execute("""SELECT array_agg(id) AS ids,
                                 product_id AS product_id,
                                 prod_spec AS prod_spec,
                                 stock_location_id as stock_location_id
                          FROM purchase_need
                          WHERE id %s
                          GROUP BY product_id, prod_spec,stock_location_id """ % (operation))
            purchase_need_list = cr.dictfetchall()
            if len(purchase_need_list) < len(active_ids):
                error_list = []
                for need in purchase_need_list:
                    if len(need['ids']) > 1:
                        error_list.append(need['ids'])
                raise osv.except_osv(
                    u'错误', u'选择了同个产品不能重复选择多条供应商记录，对应序列为：%s' % (error_list))
            location_id_list = []
            product_dict={}
            for need in purchase_need_list:
                product=self.pool.get('product.product').browse(cr, uid, need.get('product_id'), context=context)[0]
                if need.get('product_id') in product_dict and need.get('prod_spec')!=product_dict.get(need.get('product_id')):
                     raise osv.except_osv(
                         u'错误', u'相同产品不能在同一采购单中处理!')
                product_dict.update({need.get('product_id'): need.get('prod_spec')})
                if location_id_list and need['stock_location_id'] not in location_id_list:
                    raise osv.except_osv(
                        u'错误', u'一次只能处理一个库位的采购需求!')
                else:
                    location_id_list.append(need['stock_location_id'])
        return res
    # 重载向导的default_get方法，初始化数据

    def default_get(self, cr, uid, fields, context=None):
        res = super(generate_purchase_order_wizard, self).default_get(
            cr, uid, fields, context=context)
        active_ids = context.get('active_ids')

        line_data = self.get_wizard_line_messge(cr, uid, active_ids, context=context)
        res.update(line_data)
        return res

    def determine_field_vals_in_table(self, cr, uid, tablle, filed, vals, context=None):
        cr.execute("""
                select id from %s where %s in %s
        """ % (tablle, filed, vals.replace('[', '(').replace(']', ')')))
        list_vals = cr.dictfetchall()
        if len(eval(vals)) != len(list_vals):
            return False
        else:
            return True

    def determine_is_order_change(self, cr, uid, need, context=None):
        order_line_change_id = []
        choiced_so_ids = need.choiced_so_ids or '[]'
        for sale_order_ob in self.pool.get('sale.order').browse(cr, uid, eval(choiced_so_ids),
                                                                context=context):
            [order_line_change_id.append(solc.id) for solc in sale_order_ob.order_line_change_id
             if not solc.po_id and solc.product_id.id == need.product_id.id]
        # if len(order_line_change_id)!=len(eval(choiced_so_ids)): # 用于判断 没有勾选的情况
        #
        #     so_name = [sol_row.so_id.name for sol_row in
        #                self.pool.get('sale.order.line.change').browse(cr, uid,list(set(eval(need.change_ids))),context=context)]
        #     so_name_old = [so_ob.name for so_ob in
        #                self.pool.get('sale.order').browse(cr, uid, list(eval(need.so_ids)),
        #                                                   context=context)]
        #     raise osv.except_osv(u'错误', u'您采购的销售订单%s产品[%s]%s有变更，请刷新采购需求后再进行操作!' %
        #                          (list(set(so_name_old)-set(so_name),need.product_id.default_code, need.product_id.name)))

        so_change_ids = self.pool.get('sale.order.line.change').search(cr, uid,
                                                                       [('id', 'in', list(eval(need.change_ids)))],
                                                                       context=context)
        if len(so_change_ids) != len(list(eval(need.change_ids))): # 用于判断 勾选了几条的情况
            so_ids = list(eval(need.choiced_so_ids or need.so_ids))
            [order_line_change_id.append(solc_row) for solc_row in self.pool.get('sale.order.line.change').
                search(cr,uid,[('id','in',so_ids)],context=context) if solc_row in so_change_ids]
            real_so_id = [solc_ob.so_id.id for solc_ob in
                          self.pool.get('sale.order.line.change').browse(cr, uid, so_change_ids, context=context)]
            if not list(set(so_ids) - set(real_so_id)):
                return order_line_change_id
            so_name = [so_ob.name for so_ob in
                       self.pool.get('sale.order').browse(cr, uid, list(set(so_ids) - set(real_so_id)),
                                                          context=context)]
            raise osv.except_osv(u'错误', u'您采购的销售订单 %s产品[%s]%s 有变更，'
                                            u'请刷新采购需求后再进行操作!' % (so_name,need.product_id.default_code, need.product_id.name))
        return order_line_change_id

    def catch_exception(self, need):
        try:
            str(need.choiced_so_ids)
        except Exception:
            raise osv.except_osv(u" 错误", u"所选记录已经失效,请刷采购需求后重新操作!")

    def get_wizard_line_messge(self, cr, uid, active_ids, context=None):
        # 根据传入的active_ids 字段查找对应的记录 并从中获取所需要的数据
        purchase_need_obj = self.pool.get('purchase.need')
        sale_order_line_change_obj = self.pool.get('sale.order.line.change')
        purchase_need = purchase_need_obj.browse(
            cr, uid, active_ids, context=context)
        res = {}
        line_data = []
        for need in purchase_need:
            self.catch_exception(need)
            if need.choiced_so_ids and need.write_uid.id != uid:
                raise osv.except_osv(
                    u'错误', u'产品"%s"已经其他人打勾了销售订单, 要等Ta处理完或者让Ta取消打勾销售订单!'%(need.product_id.name))
            sale_note, purchase_spec = "" , ""
            order_line_change_id = self.determine_is_order_change(cr, uid, need, context=context)
            if need.change_ids:
                solc_ids = []
                if need.choiced_so_ids:
                    choiced_so_id_list = eval(need.choiced_so_ids)
                    [solc_ids.append(sale_ocl.id) for sale_ocl in
                        sale_order_line_change_obj.browse(cr, uid,order_line_change_id or eval(need.change_ids), context=context) if
                        sale_ocl.so_id.id in choiced_so_id_list]
            for line_change in sale_order_line_change_obj.browse(cr,uid,solc_ids or eval(need.change_ids.replace('{', '[').replace('}', ']')),context=context):
                    if line_change.so_line_id.sale_note:
                        sale_note += "%s %s %s\n"%(line_change.so_line_id.order_id.name,
                            int(line_change.so_line_id.product_uom_qty), line_change.so_line_id.sale_note)
                        purchase_spec += "%s： %s、" % (int(line_change.so_line_id.product_uom_qty),
                                                    line_change.so_line_id.sale_note)
            vals = {'product_id': need.product_id.id,
                    'supplier_id': need.supplier_id.id,
                    'prod_spec': need.prod_spec,
                    'sale_note': sale_note,
                    'purchase_spec': purchase_spec,
                    'change_ids': need.change_ids,
                    'price_unit': need.product_id.standard_price,
                    'so_ids': need.so_ids,
                    # 'choiced_so_ids': need.choiced_so_ids,
                    'purchase_need_id': need.id,
                    'available_qty': need.available_qty,
                    'sale_change_qty': need.sale_change_qty,
                    'non_delivery_qty': need.non_delivery_qty,
                    #'inquiry_incoming_qty': need.inquiry_incoming_qty,
                    'sale_return_not_in': need.sale_return_not_in,
                    'purchase_incoming_qty': need.purchase_incoming_qty,
                    'purchase_need_qty': need.purchase_need_qty,
                    'purchase_need_id_obj': need.id,
                    }
            line_data.append(vals)
        res.update({'wizard_line': line_data})
        return res
    # 询价单生成向导点击生成按钮

    def create_purchase_order_confirm(self, cr, uid, ids, context=None):
        po_obj = self.pool.get('purchase.order')
        pol_obj = self.pool.get('purchase.order.line')
        wizard_line_obj = self.pool.get('generate.purchase.order.wizard.line')
        sale_order_line_change_obj = self.pool.get('sale.order.line.change')
        # 因为tuple至少2个元素，所以考虑到只选择一个记录的情况，我们给ids添加一个元素但是不会影响查找结果(openerp生成的表单中的ID不会产生0)
        wizard_line_ids = wizard_line_obj.search(
            cr, uid, [('generate_wizard', 'in', ids)], context=context)
        wizard_line_ids.append(0)
        query_ids = tuple(wizard_line_ids)
        # 根据供应商，将生成的采购订单明细写入采购订单
        generate_purchase_order_wizard = self.browse(cr, uid, ids, context=context)
        # if generate_purchase_order_wizard.is_show_supplier:
        #         # 根据“相关供应商” or “其他供应商”来填充采购订单行的供应商
        if len([wizard_line.id for wizard_line in generate_purchase_order_wizard.wizard_line
                if wizard_line.purchase_need_id_obj.state == 'new']) != len(generate_purchase_order_wizard.wizard_line):
            raise osv.except_osv(
                u'错误', u'本次操作的采购需求记录已经处理过请刷新后重新操作!')
        if generate_purchase_order_wizard.supplier_id:
            wizard_line_obj.write(cr, uid, wizard_line_ids, {'supplier_id': generate_purchase_order_wizard.supplier_id.id}, context=context)
        if generate_purchase_order_wizard.other_supplier_ids:
            wizard_line_obj.write(cr, uid, wizard_line_ids, {'supplier_id': generate_purchase_order_wizard.other_supplier_ids}, context=context)
        cr.execute("""SELECT supplier_id AS supplier_id,
                                 array_agg(id) AS ids,
                                 array_agg(so_ids) AS so_ids,
                                 array_agg(change_ids) AS change_ids
                          FROM generate_purchase_order_wizard_line
                          WHERE id in %s
                          GROUP BY supplier_id""" % (str(query_ids)))
        sql_dicts = cr.dictfetchall()
        product_purchase_mes_list, purchase_order_ids = [], []
        for sql_dict in sql_dicts:
            # 按供应商分组，生成采购单
            partner_id = sql_dict['supplier_id']
            # 通过供应商获得联系人、价格表、库位等信息
            partner_onchange = po_obj.onchange_partner_id(
                cr, uid, False, partner_id, context=context)
            # 通过联系人获得电话、手机号码等信息
            contacts_onchange = {'value': {}}
            if partner_onchange['value'].__contains__('contacts_id'):
                contacts_onchange = po_obj.onchange_contacts(cr, uid, False, partner_onchange[
                    'value']['contacts_id'], context=context)
            # 获取分拣类型
            picking_type_id = self.pool.get('sale.order').get_sale_order_in_picking_type_id(cr, uid,
                                                                            eval(sql_dict['so_ids'][0]), context=context)
            picking_type_onchange = po_obj.onchange_picking_type_id(
                cr, uid, False, picking_type_id, context=context)
            # 将上面获得的数据合并成一个dict
            po_data = dict(partner_onchange['value'].items() + picking_type_onchange['value'].items()
                           + contacts_onchange['value'].items())

            if generate_purchase_order_wizard.shipping_method:
                po_data.update({'shipping_method': generate_purchase_order_wizard.shipping_method.id})
            if generate_purchase_order_wizard.planned_delivery_date:
                po_data.update({'planned_delivery_date': generate_purchase_order_wizard.planned_delivery_date})
            partner_row = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context)
            po_data.update(
                {'partner_id': partner_id, 'picking_type_id': picking_type_id,
                 'notes': generate_purchase_order_wizard.notes,
                 'is_card': any([line.product_id.is_card for line in generate_purchase_order_wizard.wizard_line]),
                 'develiey_address': generate_purchase_order_wizard.develiey_address
                                     or generate_purchase_order_wizard.shipping_method.name,
                 'is_manual_create_stock': generate_purchase_order_wizard.is_manual_create_stock})
            purchase_order_id = po_obj.create(
                cr, uid, po_data, context=context)
            purchase_order_ids.append(purchase_order_id)
            # 将选中的采购需求记录写入采购订单明细
            wizard_lines = wizard_line_obj.browse(
                cr, uid, sql_dict['ids'], context=context)

            # 判断产品采购需求数量为0时，入库状态改为“已全部入库”
            for purchase_order in po_obj.browse(cr, uid, purchase_order_id, context):
                for line in wizard_lines:
                    if line.purchase_need_qty == 0.0:
                        purchase_order.update({'pick_state': True})
                # if wizard_lines[0].purchase_need_id_obj.stock_location_id: # 取销售订单对应的order type 上面depicking_type_id
                #     purchase_order.write({'location_id': wizard_lines[0].purchase_need_id_obj.stock_location_id and wizard_lines[0].purchase_need_id_obj.stock_location_id.id})
            so_ids = []
            purchace_need_ids = []
            is_add, is_minus = False, False
            for line in wizard_lines:
                if line.purchase_need_id_obj.id in purchace_need_ids:
                    raise osv.except_osv(
                        u'错误', u'选择了相同的需求行，请核对选择后再进行生成询价单操作！')
                purchace_need_ids.append(line.purchase_need_id_obj.id)

                # 调用采购订单明细中的onchange_product_id方法，获取值
                product_onchange = pol_obj.onchange_product_id(cr, uid, False, pricelist_id=line.supplier_id.property_product_pricelist_purchase.id,
                                                               product_id=line.product_id.id,
                                                               qty=line.purchase_need_qty,
                                                               uom_id=False,
                                                               partner_id=line.supplier_id.id, context=context)

                # 通过onchange_product_id（）方法返回的“product_qty”字段值至少为1，按需求把它更新为零
                product_onchange['value'].update({
                    'product_qty': line.purchase_need_qty,
                    'price_unit': line.price_unit,
                    'purchase_spec':line.purchase_spec,
                })
                pol_data = dict({'product_id': line.product_id.id,
                                 'expected_delivery_time': po_data.get('planned_delivery_date'),
                                 'prod_spec': line.prod_spec and line.prod_spec.strip(),
                                 'order_id': purchase_order_id}, **product_onchange['value'])
                po_line_id = pol_obj.create(cr, uid, pol_data, context=context)
                # 将采购单id写回变更表
                solc_ids = []
                sale_order_change_id = self.determine_is_order_change(cr,uid,line.purchase_need_id_obj,context=context)
                if line.purchase_need_id_obj.choiced_so_ids:
                    choiced_so_id_list = eval(line.purchase_need_id_obj.choiced_so_ids)
                    so_ids +=choiced_so_id_list
                    [solc_ids.append(sale_ocl.id) for sale_ocl in sale_order_line_change_obj.browse(cr, uid, sale_order_change_id or eval(line.change_ids), context=context) if sale_ocl.so_id.id in choiced_so_id_list]
                else:
                    so_ids += eval(line.so_ids)
                    solc_ids = eval(line.change_ids)
                if solc_ids:
                    #销售订单行增加一个字段采购员“purchase_uid”，在采购单关联销售订单后(
                    # 采购助手操作后或者更多菜单里的更改销售订单关联)写入销售订单对应产品行的当前采购员purchase_uid
                    sale_order_line_ids = [change.so_line_id.id for change in
                                           sale_order_line_change_obj.browse(cr, uid, list(solc_ids), context=context)]
                    self.pool.get('sale.order.line').write(cr, uid,
                                                           sale_order_line_ids, {'purchase_uid': uid}, context=context)
                target_number = line.sale_change_qty + line.non_delivery_qty - line.available_qty - line.sale_return_not_in - line.purchase_incoming_qty
                product_purchase_mes_list.append({line.product_id.id: [list(solc_ids),
                                                                       line.prod_spec and line.prod_spec.strip(),
                                                                       purchase_order_id, target_number,
                                                                       line.purchase_need_qty, line.purchase_need_id,
                                                                       po_line_id]})

                if line.purchase_need_qty > 0:
                    is_add = True
                elif line.purchase_need_qty < 0:
                    is_minus = True
            # 先创建的销售订单 然后插入订单行 导致 对于采购单的状态的写入不对
            # (以前的设计中没有考虑这种情况 如果从源头改的话,代码量会很大,这只是比较不常见的情况所以)
            if is_add and is_minus:
                po_obj.write(cr, uid, purchase_order_id, {'is_exchange': True}, context=context)
            elif is_minus:
                po_obj.write(cr, uid, purchase_order_id, {'is_return': True}, context=context)
            if not generate_purchase_order_wizard.direct_shipment_sale_order:
                po_obj.write(cr, uid, purchase_order_id, {
                    'so_ids': list(set(so_ids))}, context=context)
            else:
                po_obj.write(cr, uid, purchase_order_id, {
                    'so_ids': str([int(generate_purchase_order_wizard.direct_shipment_sale_order)]), 'is_direct_delivery': True}, context=context)
            if generate_purchase_order_wizard.is_auto_approved:
                self.pool.get("purchase.order").signal_workflow(cr, uid, [purchase_order_id], 'purchase_confirm')
                #self.pool.get("purchase.order").write(cr, uid, [purchase_order_id], {'state': 'approved'}, context=context)
                order_ids = self.pool.get("purchase.order.line").search(cr, uid, [('order_id', 'in', [purchase_order_id])], context=context)
                self.pool.get("purchase.order.line").write(cr, uid, order_ids, {'state': 'confirmed'}, context=context)
        # 与勾选记录的产品相同记录的状态都要修改为“done”
        for product_purchase_mes in product_purchase_mes_list:
            for purchase_mes_key, purchase_mes_value in product_purchase_mes.items():
                purchase_need_objs = self.pool.get("purchase.need").browse(cr, uid, purchase_mes_value[5], context=context)
                # purchase_mes_value, 0 对应的 change 表的ids 1 ,表示 对应的 产品的型号, 2 对用的 采购单的id ,3 为 采购的需求数量
                # 4 为 本次采购的数量, 5 是 本条记录对应的 采购需求表的id ,6 采购单行的id
                # 如果 本次采购的数量大于 采购需求量 则 直接 对记录进行处理, 否则 拆分change 记录
                if purchase_mes_value[3] > purchase_mes_value[4]:
                    middle_qty = purchase_mes_value[4]
                    sale_order_lc_ids = sale_order_line_change_obj.search(cr, uid, [('id', 'in', purchase_mes_value[0])],
                                                                          order="expected_delivery_datetime,change_quantity", context=context)
                    middle_qty_change_ids = purchase_mes_value[0]
                    socl_check_id = sale_order_line_change_obj.search(cr, uid, [('id', 'in', purchase_mes_value[0])],
                                                                      context=context)
                    if purchase_need_objs.state != 'new' and not socl_check_id:
                        raise osv.except_osv(
                            u'错误', u'本次操作的采购需求记录已经处理过!\n请刷新需求后重新操作!')

                    for sale_order_line_change in sale_order_line_change_obj.browse(cr, uid, sale_order_lc_ids, context=context):
                        middle_qty_change_ids.remove(sale_order_line_change.id)
                        if middle_qty >= sale_order_line_change.change_quantity:
                            middle_qty = middle_qty - sale_order_line_change.change_quantity
                            sale_order_line_change.write({'po_id': purchase_mes_value[2], 'po_line_id': purchase_mes_value[6]})
                        elif middle_qty > 0:
                            # 对 本次采购的数量不满足一整条销售订单变更历史的情况下 进行拆分销售订单变更历史记录
                            original_change_num = sale_order_line_change.change_quantity
                            sale_order_line_change.write({'po_id': purchase_mes_value[2], 'change_quantity': middle_qty, 'po_line_id': purchase_mes_value[6]})
                            sale_order_line_id = sale_order_line_change_obj.create(
                                cr, uid, {'product_id': sale_order_line_change.product_id.id, 'so_id': sale_order_line_change.so_id.id, "so_line_id": sale_order_line_change.so_line_id.id,
                                          'change_quantity': original_change_num - middle_qty, "prod_spec": sale_order_line_change.prod_spec,
                                          'expected_delivery_datetime': sale_order_line_change.expected_delivery_datetime,
                                          'stock_location_id': sale_order_line_change.stock_location_id.id,
                                          'stock_picking_type_id': sale_order_line_change.stock_picking_type_id.id},
                                context=context)
                            middle_qty_change_ids.append(sale_order_line_id)
                            break
                        else:
                            break

                    purchase_need_objs.write({'state': 'done'})

                else:
                    socl_check_id=sale_order_line_change_obj.search(cr,uid,[('id','=',purchase_mes_value[0])],context=context)
                    if purchase_need_objs.state != 'new' and not socl_check_id:
                        raise osv.except_osv(
                            u'错误', u'本次操作的采购需求记录已经处理过!\n请刷新需求后重新操作!')
                    sale_order_line_change_obj.write(
                        cr, uid, purchase_mes_value[0], {'po_id': purchase_mes_value[2], 'po_line_id': purchase_mes_value[6]}, context=context)
                    purchase_need_objs.write({'state': 'done'})
                if purchase_need_objs.purchase_need_qty == 0:
                    purchase_need_objs.write({'state': 'done'})

        if generate_purchase_order_wizard.is_auto_create_incoming:
            # 如果界面上勾选了自动生成入库单 则执行 生成入库单的操作
            self.pool.get('batch.generation.stock.picking').generation_create_stock_picking(cr, uid, purchase_order_ids, context=context)
        # if generate_purchase_order_wizard.direct_shipment_sale_order:
        #     self.create_sale_order_out_picking(cr, uid, purchase_order_ids, context=context)

        # 下边的代码就是标准的 根据id 返回对应id的视图 一个id时是form视图，多个的情况是 tree
        """ 刷新采购需求 ，这步操作会引起 旧的已经改变的采购的 的删除 """
        self.pool.get('purchase.need.refresh.wizard').sql_get_purchase_need_result(cr, uid, False,
                                                                                   False, context=context)
        mod_obj = self.pool.get('ir.model.data')
        act_obj = self.pool.get('ir.actions.act_window')
        result = mod_obj.get_object_reference(
            cr, uid, 'purchase', 'purchase_form_action')
        id = result and result[1] or False
        result = act_obj.read(cr, uid, [id], context=context)[0]
        # compute the number of invoices to display

        if len(purchase_order_ids) > 1:
            result[
                'domain'] = "[('id','in',[" + ','.join(map(str, purchase_order_ids)) + "])]"
        else:
            res = mod_obj.get_object_reference(
                cr, uid, 'purchase', 'purchase_order_form')
            result['views'] = [(res and res[1] or False, 'form')]
            result['res_id'] = purchase_order_ids and purchase_order_ids[0] or False
        return result

    def create_sale_order_out_picking(self, cr, uid, purchase_ids, context=None):
        sale_obj = self.pool.get('sale.order')
        wizard_obj = self.pool.get("confirm.out.going.wizard")
        model_data_obj = self.pool.get('ir.model.data')
        direct_location_id = model_data_obj.xmlid_to_res_id(cr, SUPERUSER_ID, 'dftg_ext.stock_location_direct')
        for purchase_order in self.pool.get('purchase.order').browse(cr, uid, purchase_ids, context=context):
            if purchase_order.so_ids and purchase_order.is_direct_delivery:
                so_ids = eval(purchase_order.so_ids)
                if len(so_ids) != 1:
                    return True
                product_dict = {}
                for line in purchase_order.order_line:
                    product_dict.update({line.product_id.id: line.product_qty})
                order = sale_obj.browse(cr, uid, so_ids, context=context)
                if order.is_need_purchase_audit: # 直送订单不需要检查送货日期
                    sale_obj.confirm_expected_delivery_time(cr, uid, order.id, context=context)
                for line in order.order_line:
                    if line.product_id.id in product_dict and line.product_uom_qty >0:
                        line.write({'delivery_quantity_qty': product_dict.get(line.product_id.id)})
                    else:
                        line.write({'delivery_quantity_qty': 0})
                sale_obj.out_going_confirm(cr, uid, [order.id], context=context)
                wizard_id = wizard_obj.create(cr, uid, {'sale_order_id': order.id,
                                                        'po_id': purchase_order.id,
                                                        'location_id': direct_location_id,
                                                        }, context=context)
                wizard_obj.confirm_out_going(cr, uid, [wizard_id], context=dict({'default_sale_order_id': order.id,
                                                                                 'on_move': True} ,
                                                                                **context))

            return True
            # if purchase_order.

    def _select_other_supplier_ids(self, cr, uid, ids, context=None):
        # 502 采购生成询价单向导页面增加一个供应商选择框，叫“相关供应商选择”，选择范围是下方所有产品设置过的所有供应商列表
        partner_obj = self.pool.get('res.partner')
        template_obj = self.pool.get('product.template')
        supplier_list = []
        supplier_partner_list = []
        for needs in self.pool.get('purchase.need').browse(cr, uid, context.get("active_ids"), context=context):
            self.catch_exception(needs)
            product_ids = template_obj.search(cr, uid, [('id', '=', needs.product_id.id)], context=context)
            for product in template_obj.browse(cr, uid, product_ids, context=context):
                for seller in product.seller_ids:
                    supplier_list.append(seller.id)
        for supplier in self.pool.get('product.supplierinfo').browse(cr, uid, list(set(supplier_list)), context=context):
            partner_id = partner_obj.search(cr, uid, [('id', '=', supplier.name.id)], context=context)
            if partner_id:
                supplier_partner_list.append(partner_id[0])

        partner_inistance = partner_obj.browse(cr, uid, set(supplier_partner_list), context=context)
        return self.pool.get('res.partner').name_get(cr, uid, [partner.id for partner in partner_inistance], context=context)

    def onchange_other_supplier_ids(self, cr, uid, ids, other_supplier_ids, supplier_id, context=None):
        # 当填充“相关供应商”时，把“其他供应商”清空
        if supplier_id:
            if other_supplier_ids:
                return {'value': {'supplier_id': ''}}
        if not other_supplier_ids and not supplier_id:
            return

    def onchange_supplier_ids(self, cr, uid, ids, other_supplier_ids, supplier_id, context=None):
        # 当填充“其他供应商”时，把“相关供应商”清空
        if supplier_id:
            if other_supplier_ids:
                return {'value': {'other_supplier_ids': ''}}
        if not other_supplier_ids and not supplier_id:
            return

    def on_change_is_auto_create_incoming(self, cr, uid, ids, is_auto_create_incoming, context=None):
        if is_auto_create_incoming:
            return {
                "value": {'is_auto_approved': True, 'is_auto_create_incoming': False, 'is_manual_create_stock': False}}

    def on_change_is_auto_approved(self, cr, uid, ids, is_auto_approved, context=None):
        if is_auto_approved:
            return {"value": {'is_manual_create_stock': False}}

    def _get_sale_order_list(self, cr, uid, ids, context=None):
        # 用这个方法获取选中的 purchase.need 的表中数据行中so_ids 字段的集合，为了特殊的显示，以至于
        # 在采购单上显示清晰的
        wizard_line_messge = self.get_wizard_line_messge(cr, uid, context.get("active_ids"), context=context)
        sale_order_obj = self.pool.get("sale.order")
        sale_order_line_ids, sale_order_id = [], []
        for wizard_line in wizard_line_messge.get("wizard_line"):
            sale_order_line_ids.extend(eval(wizard_line.get("so_ids", [])))
        sale_order_objs = sale_order_obj.browse(cr, uid, sale_order_line_ids, context=context)
        sale_order_id.extend([(order.id, order.name + str((order.partner_id.name))) for order in sale_order_objs])
        return sale_order_id

    def on_change_direct_shipment_sale_order(self, cr, uid, ids, direct_shipment_sale_order, context=None):
        """# 854 生成询价单向导页面送货方式下方增加送货地址字段（采购单form中已经加过了，onchange需要同样的逻辑），
        如果选择了直送销售订单onchange，需要把销售订单的送货地址、收货人、手机、电话号码拼接成一个字符串填充在送货地址里 [Issue开启]"""
        if direct_shipment_sale_order:
            sale_order_objs = self.pool.get('sale.order').browse(cr, uid, direct_shipment_sale_order, context=context)
            address = ""
            wizard_line_messge = self.get_wizard_line_messge(cr, uid, context.get("active_ids"), context=context)
            sale_order_obj = self.pool.get("sale.order")
            sale_order_line_ids, sale_order_id = [], []
            for wizard_line in wizard_line_messge.get("wizard_line"):
                sale_order_line_ids.extend(eval(wizard_line.get("choiced_so_ids", False) or wizard_line.get("so_ids", '[]')))
            order_objs = sale_order_obj.browse(cr, uid, sale_order_line_ids, context=context)
            if len(set([order.id for order in order_objs])) > 1:
                return {'value': {'develiey_address': '', 'direct_shipment_sale_order': ''},
                        'warning': {'title': u'警告', 'message': u'只能单个销售订单的采购才能直送！'}}
            if sale_order_objs.county_id and sale_order_objs.street and sale_order_objs.county_id.county_name not in sale_order_objs.street:
                address = "%s %s" % (sale_order_objs.county_id.county_name, sale_order_objs.street)
            else:
                address = sale_order_objs.street
            return {'value': {'develiey_address': "%s %s  %s    %s" % (address, sale_order_objs.consignee, sale_order_objs.mobile_number,
                                                                       sale_order_objs.phone_number)}}
        else:
            return {'value': {'develiey_address': ""}}
        return True

    def onchange_shipping_method(self, cr, uid, ids, shipping_method, context=None):
        """ 采购单form送货方式字段标签文字改为“常用送货地址”，
        增加一个字段在下面：“送货地址”develiey_address，常用送货地址选择后填充送货地址develiey_address [Issue开启]"""
        shipping_method_objs = self.pool.get('shipping.method').browse(cr, uid, shipping_method, context=context)
        if shipping_method_objs:
            return {'value': {
                'develiey_address': shipping_method_objs[0].name
            }}

    def on_change_is_manual_create_stock(self, cr, uid, ids, is_manual_create_stock, context=None):
        if is_manual_create_stock: #手动生成入库单操作依旧保留，只是生成的采购单不确定了。（要想生成出入库单只能手动点击确认）
            return {'value': {
                'is_auto_approved': False
            }}
    _columns = {
        'wizard_line': fields.one2many('generate.purchase.order.wizard.line', 'generate_wizard', string=u'询价单生成向导明细'),
        "supplier_id": fields.many2one("res.partner", domain=[("supplier", "=", True)], string="供应商"),
        'is_show_supplier': fields.boolean(u'是否显示供应商'),
        'other_supplier_ids': fields.selection('_select_other_supplier_ids', type="many2one", relation="res.partner", string=u'相关供应商选择'),
        'is_auto_approved': fields.boolean(u'询价单是否自动确认'),
        'is_auto_create_incoming': fields.boolean(u'自动生成入库单'),
        'direct_shipment_sale_order': fields.selection('_get_sale_order_list', string="直送销售订单"),
        'shipping_method': fields.many2one('shipping.method', domain=[('is_active', '=', True)], string=u'送货方式'),
        'planned_delivery_date': fields.date(u'计划发货日期'),
        'notes': fields.char(u'采购单备注'),
        'develiey_address': fields.char(u'送货或者提货地址'),
        'is_manual_create_stock':fields.boolean(u'手动生成入库单'),
    }
    def _set_default_shipping_method(self,cr,uid,context=None):
        method_ids = self.pool.get('shipping.method').search(cr,uid,[('is_default','=',True)],context=context)
        if method_ids:
            return method_ids[0]
    _defaults = {
        'shipping_method':_set_default_shipping_method,
        'is_auto_create_incoming': False,  # 以前自动生成（出）入库单的设置取消，防止和加入的确定后生成入库单 冲突
        'is_auto_approved': True   #这边和确定后生成入库单 同步。
    }

class generate_purchase_order_wizard_line(osv.osv_memory):
    _name = 'generate.purchase.order.wizard.line'
    _description = u'询价单生成向导明细'

    def on_change_prodcut_id(self, cr, uid, ids, product_id, prod_spec, context=None):
        """此方法已经废弃,不用了.功能同 on_change_purchase_need_id"""
        if product_id:

            cr.execute("""SELECT id ,prod_spec
                          FROM purchase_need
                          WHERE product_id=%s and state='new'
                          """ % (product_id))
            purchase_need_list = cr.dictfetchall()
            if purchase_need_list:
                for purchase_need in purchase_need_list:
                    if len(purchase_need_list) == 1:
                        purchase_need_row = self.pool.get('purchase.need').browse(cr, uid, purchase_need.get('id'), context=context)
                        self.pool.get('generate.purchase.order.wizard').catch_exception(purchase_need_row)
                        return {'value': {'product_id': purchase_need_row.product_id.id,
                                          'supplier_id': purchase_need_row.supplier_id.id,
                                          'prod_spec': purchase_need_row.prod_spec,
                                          'change_ids': purchase_need_row.change_ids,
                                          'price_unit': purchase_need_row.product_id.standard_price,
                                          'so_ids': purchase_need_row.so_ids,
                                          'purchase_need_id': purchase_need_row.id,
                                          'available_qty': purchase_need_row.available_qty,
                                          'sale_change_qty': purchase_need_row.sale_change_qty,
                                          'non_delivery_qty': purchase_need_row.non_delivery_qty,
                                          #'inquiry_incoming_qty': purchase_need_row.inquiry_incoming_qty,
                                          'sale_return_not_in': purchase_need_row.sale_return_not_in,
                                          'purchase_incoming_qty': purchase_need_row.purchase_incoming_qty,
                                          'purchase_need_qty': purchase_need_row.purchase_need_qty,
                                          'prod_spec': purchase_need.get('prod_spec')}}
                    if purchase_need.get('id') and(prod_spec == purchase_need.get('prod_spec')or((not prod_spec) and purchase_need.get('prod_spec') is None)):
                        purchase_need_row = self.pool.get('purchase.need').browse(cr, uid, purchase_need.get('id'), context=context)
                        return {'value': {'product_id': purchase_need_row.product_id.id,
                                          'supplier_id': purchase_need_row.supplier_id.id,
                                          'prod_spec': purchase_need_row.prod_spec,
                                          'change_ids': purchase_need_row.change_ids,
                                          'price_unit': purchase_need_row.product_id.standard_price,
                                          'so_ids': purchase_need_row.so_ids,
                                          'purchase_need_id': purchase_need_row.id,
                                          'available_qty': purchase_need_row.available_qty,
                                          'sale_change_qty': purchase_need_row.sale_change_qty,
                                          'non_delivery_qty': purchase_need_row.non_delivery_qty,
                                          #'inquiry_incoming_qty': purchase_need_row.inquiry_incoming_qty,
                                          'sale_return_not_in': purchase_need_row.sale_return_not_in,
                                          'purchase_incoming_qty': purchase_need_row.purchase_incoming_qty,
                                          'purchase_need_qty': purchase_need_row.purchase_need_qty,
                                          'prod_spec': purchase_need.get('prod_spec')}}

        return {'value': {}}

    def on_change_purchase_need_id(self, cr, uid, ids, purchase_need_id_obj, context=None):
        """onchange 当用户在生成询价单向导页面想要添加一行generate_purchase_order_wizard_line
        记录时 通过选择 purchase_need_id 然后通过本onchange' 自动填充剩余的数据"""
        if purchase_need_id_obj:
            purchase_need_row = self.pool.get('purchase.need').browse(cr, uid, purchase_need_id_obj, context=context)
            self.pool.get('generate.purchase.order.wizard').catch_exception(purchase_need_row)
            return {'value': {'product_id': purchase_need_row.product_id.id,
                              'supplier_id': purchase_need_row.supplier_id.id,
                              'prod_spec': purchase_need_row.prod_spec,
                              'change_ids': purchase_need_row.change_ids,
                              'price_unit': purchase_need_row.product_id.standard_price,
                              'so_ids': purchase_need_row.so_ids,
                              'purchase_need_id': purchase_need_row.id,
                              'available_qty': purchase_need_row.available_qty,
                              'sale_change_qty': purchase_need_row.sale_change_qty,
                              'non_delivery_qty': purchase_need_row.non_delivery_qty,
                              #'inquiry_incoming_qty': purchase_need_obj.inquiry_incoming_qty,
                              'sale_return_not_in': purchase_need_row.sale_return_not_in,
                              'purchase_incoming_qty': purchase_need_row.purchase_incoming_qty,
                              'purchase_need_qty': purchase_need_row.purchase_need_qty,
                              'prod_spec': purchase_need_row.prod_spec}}
        return {'value': {'product_id': '',
                          'supplier_id': '',
                          'prod_spec': '',
                          'change_ids': '',
                          'price_unit': '',
                          'so_ids': '',
                          'purchase_need_id': '',
                          'available_qty': '',
                          'sale_change_qty': '',
                          'non_delivery_qty': '',
                          'sale_return_not_in': '',
                          'purchase_incoming_qty': '',
                          'purchase_need_qty': '',
                          'prod_spec': ''}}
    _columns = {
        'generate_wizard': fields.many2one('generate.purchase.order.wizard', string=u"询价单生成向导"),
        'purchase_need_id_obj': fields.many2one('purchase.need', string=u"采购需求行", domain=[('state', '=', 'new')]),
        'product_id': fields.many2one('product.product', string=u'产品'),
        'supplier_id': fields.many2one('res.partner', string=u'供应商'),
        'purchase_spec':fields.char(u'采购备注'),
        'prod_spec': fields.char(u'型号备注'),
        'purchase_need_id': fields.integer(u'采购需求表id'),
        'change_ids': fields.char(string=u'变更表ID'),
        'so_ids': fields.char(string=u'销售订单ID'),
        'price_unit': fields.float(string=u'价格'),
        'available_qty': fields.float(string=u'已有库存数', digits_compute=dp.get_precision('Product Unit of Measure')),
        'sale_change_qty': fields.float(string=u'订单变更数', digits_compute=dp.get_precision('Product Unit of Measure')),
        'non_delivery_qty': fields.float(string=u'未出库数', digits_compute=dp.get_precision('Product Unit of Measure')),
        #'inquiry_incoming_qty': fields.float(string=u'已下询价单未到货数', digits_compute=dp.get_precision('Product Unit of Measure')),
        'sale_return_not_in': fields.float(string=u'销售退货数', digits_compute=dp.get_precision('Product Unit of Measure')),
        'purchase_incoming_qty': fields.float(string=u'已下采购单未到货数', digits_compute=dp.get_precision('Product Unit of Measure')),
        'purchase_need_qty': fields.float(string=u'采购需求量', digits_compute=dp.get_precision('Product Unit of Measure')),
        'sale_note': fields.text(u'销售备注'),
    }


class batch_generation_stock_picking(osv.osv_memory):
    _name = 'batch.generation.stock.picking'
    # 在采购单列表处 选取几个没有生成过入（出，退货）库单的采购单 进行批量生成入（出，退货）库单
    _columns = {
        'purchase_order_name': fields.text(u'要生成入库单的列表')

    }
    _defaults = {
        "purchase_order_name": lambda s, cr, u, ctx={}: '、'.join([purchase.name for purchase in s.pool.get('purchase.order').browse(cr, u, ctx.get('active_ids'), context=ctx)])
    }

    def fields_view_get(self, cr, uid, view_id=None, view_type='form',
                        context=None, toolbar=False, submenu=False):
        # 对选取的采购单进行条件的判断 采购单不存在采购单行则不允许操作 ，采购单 没有确定 就不能生成入库单
        if context is None:
            context = {}
        res = super(batch_generation_stock_picking, self).fields_view_get(
            cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=False)
        if context.get('active_model', '') == 'purchase.order' and len(context['active_ids']) >= 1:
            for purchase_order in self.pool.get('purchase.order').browse(cr, uid, context['active_ids'], context=context):
                # if purchase_order.is_manual_create_stock:
                #     raise osv.except_osv(u'警告!',
                #                          u'%s手动生成入库单的采购单不允许批量生成入库单！'%purchase_order.name)
                # if purchase_order.invoice_method=='picking':
                #     raise osv.except_osv(u'警告!',
                #                          u'基于入库单的采购单不能批量生成入库单！')
                if not purchase_order.order_line:
                    raise osv.except_osv(u'警告!',
                                         u'%s 采购单不存在订单行,不需要生成入库单' % (purchase_order.name))
                is_create_enough = True
                for order_line in purchase_order.order_line:
                    if abs(order_line.product_qty) > order_line.delivered_qty:
                        is_create_enough = False
                if is_create_enough:
                    raise osv.except_osv(u'警告!',
                                         u'%s 采购单已建立足够的入(出)库单,请核对后再进行操作!' % (purchase_order.name))
                if purchase_order.state == 'draft':
                    raise osv.except_osv(u'警告!',
                                         u'%s 采购单在草稿状态不能生成入(出)库单' % (purchase_order.name))
                elif purchase_order.state == 'cancel':
                    raise osv.except_osv(u'警告!',
                                         u'%s 采购单在取消状态不能生成入(出)库单' % (purchase_order.name))

                # raise osv.except_osv(
                # _('Warning!'), u'%s销售订单已有假单' % (purchase_order.name))
        return res

    def generation_stock_picking(self, cr, uid, ids, context=None):
        # 批量生成入库单 返回生成入库单的列表，
        picking_in_ids = self.generation_create_stock_picking(cr, uid, context.get('active_ids'), context=context)
        if picking_in_ids:
            model_data_obj = self.pool.get('ir.model.data')
            stock_picking_tree_ids = model_data_obj.xmlid_to_res_id(
                cr, SUPERUSER_ID, 'dftg_stock_picking_tree')
            view_picking_form_id = model_data_obj.xmlid_to_res_id(
                cr, SUPERUSER_ID, 'stock.view_picking_form')
            
            return {
                'type': 'ir.actions.act_window',
                'res_model': 'stock.picking',
                'view_mode': 'tree,form',
                'view_type': 'form',
                'name': u'查看出/入库单',
                'views': [(stock_picking_tree_ids, 'tree'), (view_picking_form_id, 'form')],
                'view_id': stock_picking_tree_ids,
                'domain': "[('id','in',[" + ','.join(map(str, picking_in_ids)) + "])]",
            }
        return True
    
    def get_in_out_or_service_flag(self, cr, uid, purchase, context=None):
        picking_in_singe = False
        picking_out_singe = False
        picking_in_service = True
        picking_out_service = True
        for order_line in purchase.order_line:
            if order_line.product_qty - order_line.delivered_qty > 0:
                picking_in_singe = True
                if order_line.product_id.type !='service':
                    picking_in_service= False
            elif order_line.product_qty - order_line.delivered_qty < 0:
                if order_line.product_id.type != 'service':
                    picking_out_service = False
                picking_out_singe = True
        return picking_in_singe, picking_out_singe, picking_in_service, picking_out_service

    def get_common_picking_val(self, cr, uid, purchase, context=None):
        res = {"partner_id": purchase.partner_id.id,
               "origin": purchase.name,
               "move_type": "one"}
        if purchase.partner_id.multi_address_ids:
            address = purchase.partner_id.multi_address_ids[0]
            res.update({'consignee': address.name, 'street': address.street,
                        'zip': address.zip, 'mobile_number': address.phone,
                        'phone_number': address.phone_number, 'province_id': address.province_id.id,
                        'city_id': address.city_id.id, 'county_id': address.county_id.id})
        return res
    
    def generation_create_stock_picking(self, cr, uid, ids, context=None):
        # 根据传入的采购单的 ids 进行 生成 入库单-或者出库单
        stock_picking_type_obj = self.pool.get("stock.picking.type")
        stock_picking_obj = self.pool.get("stock.picking")
        purchase_order_obj = self.pool.get('purchase.order')
        picking_type_in_id = stock_picking_type_obj.search(
            cr, uid, [('code', '=', 'incoming')], context=context)
        picking_type_out_id = stock_picking_type_obj.search(
            cr, uid, [('code', '=', 'outgoing')], context=context)
        picking_type_in_obj = stock_picking_type_obj.browse(
            cr, uid, picking_type_in_id, context=context)[0]  # warehouse_id
        picking_type_out_obj = stock_picking_type_obj.browse(
            cr, uid, picking_type_out_id, context=context)[0]  # warehouse_id
        picking_in_ids, picking_out_ids = [], []
        for purchase in purchase_order_obj.browse(cr, uid, ids, context=context):
            res = self.get_common_picking_val(cr, uid, purchase, context=context)
            purchase_order_obj.write(cr, uid, purchase.id, {
                'is_picking_button_assigned': True}, context=context)
            picking_in_singe, picking_out_singe, picking_in_service,\
            picking_out_service = self.get_in_out_or_service_flag(cr, uid, purchase, context=context)
            picking_in_id , picking_out_id = False, False
            if picking_out_service and picking_in_service:
                cr.execute("""UPDATE purchase_order SET pick_state=True WHERE id=%s""", (purchase.id, ))
                return False
            if picking_in_singe and not picking_in_service:
                res.update({"picking_type_id": picking_type_in_id[0], 'name': self.pool.get(
                    'ir.sequence').get_id(cr, uid, picking_type_in_obj.sequence_id.id, 'id', context=context)})
                picking_in_id = stock_picking_obj.create(
                    cr, uid, res, context=context)
                picking_in_ids.append(picking_in_id)
            if picking_out_singe and not picking_out_service:
                res.update({"picking_type_id": picking_type_out_id[0], 'name': self.pool.get(
                    'ir.sequence').get_id(cr, uid, picking_type_out_obj.sequence_id.id, 'id', context=context),
                            'is_card': purchase.is_card
                            })
                picking_out_id = stock_picking_obj.create(
                    cr, uid, res, context=context)
                picking_out_ids.append(picking_out_id)
                # 448
                # 东方团购采购单根据“批量生成入库单”向导生成的入库单的“reception_to_invoice”值应该为True，否则采购-发票管理-基于收到的发票菜单进去看不到自动生成的入库单
                # 经过产看源码的实现机制 发现move_line上的purchase_line_id字段有值才会填写
                # reception_to_invoice
            self.create_move_line(cr, uid, purchase, picking_in_id, picking_out_id, context=context)
            # if not move_line_out_data and not move_line_in_data:
            #     raise osv.except_osv(u'错误', u'不需要出(入)库操作,请核对后再进行操作!\n 1.纯服务类采购单！\n2.采购订单行数量全为量为零')
            if picking_in_ids:
                stock_picking_obj.action_confirm(
                    cr, uid, picking_in_ids, context=context)
            if picking_out_ids:
                stock_picking_obj.action_confirm(
                    cr, uid, picking_out_ids, context=context)
            return_ids = []
            if picking_in_ids:
                return_ids.extend(picking_in_ids)
            if picking_out_ids:
                return_ids.extend(picking_out_ids)
        return return_ids


    def create_move_line(self, cr, uid, purchase, picking_in_id, picking_out_id,context=None):
        stock_picking_type_obj = self.pool.get("stock.picking.type")
        stock_move_obj = self.pool.get("stock.move")
        purchase_order_obj = self.pool.get('purchase.order')
        model_data_obj = self.pool.get('ir.model.data')
        suppliers_location_id = model_data_obj.xmlid_to_res_id(
            cr, SUPERUSER_ID, 'stock.stock_location_suppliers')
        picking_type_in_id = stock_picking_type_obj.search(
            cr, uid, [('code', '=', 'incoming')], context=context)
        picking_type_out_id = stock_picking_type_obj.search(
            cr, uid, [('code', '=', 'outgoing')], context=context)
        picking_type_in_obj = stock_picking_type_obj.browse(
            cr, uid, picking_type_in_id, context=context)[0]  # warehouse_id
        picking_type_out_obj = stock_picking_type_obj.browse(
            cr, uid, picking_type_out_id, context=context)[0]  # warehouse_id
        for order_line in purchase.order_line:
            if order_line.product_id.type != 'service':
                if order_line.product_qty - order_line.delivered_qty > 0:
                    move_line_in_data = {"picking_type_id": picking_type_in_id[0],
                                              "product_uom_qty": order_line.product_qty - order_line.delivered_qty,
                                              "product_id": order_line.product_id.id, "name": order_line.name,
                                              "location_dest_id": purchase_order_obj.get_direct_location(cr) \
                                                  if purchase.is_direct_delivery else purchase.location_id.id,
                                              'origin': purchase.name,
                                              "location_id": picking_type_in_obj.default_location_src_id.id,
                                              "picking_id": picking_in_id,
                                              "product_uom": order_line.product_id.uom_id.id,
                                              'prod_spec': order_line.prod_spec,
                                              'invoice_state': purchase.invoice_method == 'picking' and '2binvoiced'
                                                               or 'none',
                                              'purchase_line_id': order_line.id,
                                              'warehouse_id': purchase.picking_type_id.warehouse_id.id,
                                              'route_ids': purchase.picking_type_id.warehouse_id and
                                                           [(6, 0, [x.id for x in
                                                                    purchase.picking_type_id.warehouse_id.route_ids])]
                                                           or [],
                                              'company_id': purchase.company_id.id,
                                              'is_direct_delivery': purchase.is_direct_delivery,
                                              'partner_id': order_line.order_id.partner_id.id,
                                              'date_expected': fields.date.date_to_datetime(self, cr, uid,
                                                                                            order_line.date_planned,
                                                                                            context), }
                
                    stock_move_obj.create(
                        cr, uid, move_line_in_data, context=context)
                    order_line.write({'delivered_qty': order_line.product_qty})
                elif order_line.product_qty + order_line.delivered_qty < 0:
                    move_line_out_data = {"picking_type_id": picking_type_out_id[0],
                                               "product_uom_qty": abs(
                                                   order_line.product_qty) - order_line.delivered_qty,
                                               "product_id": order_line.product_id.id, "name": order_line.name,
                                               "location_dest_id": suppliers_location_id,
                                               'origin': purchase.name,
                                               "location_id": purchase.location_id.id or picking_type_out_obj.default_location_src_id.id,
                                               "picking_id": picking_out_id,
                                               "product_uom": order_line.product_id.uom_id.id,
                                               'prod_spec': order_line.prod_spec,
                                               'invoice_state': purchase.invoice_method == 'picking' and
                                                                '2binvoiced' or 'none',
                                               'purchase_line_id': order_line.id,
                                               'warehouse_id': purchase.picking_type_id.warehouse_id.id,
                                               'route_ids': purchase.picking_type_id.warehouse_id and
                                                            [(6, 0, [x.id for x in
                                                                     purchase.picking_type_id.warehouse_id.route_ids])] or [],
                                               'company_id': purchase.company_id.id,
                                               'date_expected': fields.date.date_to_datetime(self, cr, uid,
                                                                                             order_line.date_planned,
                                                                                             context), }
                
                    stock_move_obj.create(
                        cr, uid, move_line_out_data, context=context)
                    order_line.write({'delivered_qty': abs(order_line.product_qty)})
               
        return True